コンパイラとは?
コンパイラとは、プログラミング言語で書かれたソースコードを、プログラムの実行前に機械語やバイトコードなどの低水準の形式に翻訳するプログラムです。翻訳されたプログラムは、元のソースがなくても実行できます。
更新日: 2026年9月24日
- 1print("Hello")
- 2total = 2 + 3
- 3print(total)
- 4print("Done")
一度コンパイルすれば、プログラムは何度でも実行できます。実行回数は増えますが、コンパイル回数は増えません。つづり間違いをオンにすると、コンパイラはプログラム全体を受け付けないので、何も表示されません。
次のCプログラムを hello.c として保存します。
#include <stdio.h>
int main(void) {
int price = 4;
printf("Total: %d\n", price * 3);
return 0;
}
続いて gcc hello.c -o hello と入力します。しばらく何も起きないように見えますが、やがてソースの隣に hello という新しいファイルが現れます。これがコンパイラの出力で、プロセッサが実行できる機械語です。hello.c を削除しても ./hello は動きます。プログラムに必要なものは、すでにすべて翻訳済みだからです。
gcc hello.c -o hello
./hello
Total: 12
コンパイラの仕組みを順番に
コンパイラはソースコードをプレーンテキストとして読み、いくつかの段階を経て処理します。total = price * 3 という1行で見てみましょう。
- 字句解析。 テキストを、言語の「単語」にあたるトークンに分割します。名前
total、演算子=、名前price、演算子*、数値3です。空白やコメントはここで捨てられます。 - 構文解析。 トークンを構文木に組み立て、構造を記録します。値が
priceと3の掛け算である代入文です。言語の文法に反するコードは、ここで構文エラーとして止まります。 - 意味解析。 コンパイラは意味をチェックします。
priceは宣言されているか、型は合っているか、呼び出している関数は存在するか。 - 最適化。 動作を変えずに、より速く、あるいはより少ないメモリで動くようにプログラムを書き換えます。
x * 2が1つのシフト命令になったり、決して実行されないコードが取り除かれたりします。 - コード生成。 コンパイラは対象に合わせた出力を書き出します。x86-64やARMの機械語、あるいは仮想マシン用のバイトコードです。
CとC++では最後にリンクという段階があり、コンパイルしたファイルとライブラリのコード(Cの標準ライブラリの printf など)を1つの実行ファイルにまとめます。gcc はリンカも代わりに実行してくれるので、1つのコマンドのように感じられます。
Pythonは最初の2段階を標準モジュールとして提供しているので、実際の入力で動きを見ることができます。
NAME 'total'
OP '='
NAME 'price'
OP '*'
NUMBER '3'
Assign(
targets=[
Name(id='total', ctx=Store())],
value=BinOp(
left=Name(id='price', ctx=Load()),
op=Mult(),
right=Constant(value=3)))
この木が、コンパイラの残りの段階が扱う材料です。もう空白や改行は気にせず、構造だけを見ています。price に 3 を掛けた結果を total に代入する、という構造です。
コンパイル時: 実行前に見つかるエラー
コンパイラはプログラムの一部でも実行する前に全体を読むので、間違いを早い段階で拒否できます。次の例では、source の中の3つの print 行はどれも実行されません。最初の行さえもです。実行が始まる前に、Pythonのコンパイラがテキストを拒否するからです。
Rejected before running anything:
'(' was never closed (line 3)
静的型付け言語のコンパイラは、構文以外の間違いも見つけます。bad.c というファイルにある次のCの行は文法的には正しいのですが、整数の変数に文字列を入れようとしています。
int count = "five";
GCC 14以降ではエラーで止まります。
bad.c:4:17: error: initialization of 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]
Pythonで同じ間違いをしても、その行が実行されたときに初めて表に出ます。コンパイル時に見つけられることは、大きなチームがコンパイル型の静的型付け言語を選ぶ理由の1つです。ただし、コンパイラがすべてを見つけられるわけではありません。結果的にゼロになった数で割ることや、存在しないファイルを読むことは、やはり実行時に失敗します。
コンパイラの例
| コンパイラ | 読むもの | 出力するもの |
|---|---|---|
| GCC, Clang | C, C++ | x86-64、ARMなど向けの機械語 |
| MSVC (Microsoft Visual C++) | C, C++ | Windowsの実行ファイル(.exe, .dll) |
javac | Java | JVMバイトコード(.class ファイル) |
rustc | Rust | 機械語 |
go build | Go | 1つの実行ファイル |
tsc | TypeScript | JavaScript |
このうち2つの行からわかるように、出力は機械語だけではありません。javac が出力するバイトコードは、Javaの実行環境の一部であるJava仮想マシンが実行します。tsc はTypeScriptを別の高水準言語であるJavaScriptに変換します。このような変換をするコンパイラは、トランスパイラと呼ばれることがよくあります。
コンパイルされた機械語は、1つのCPUファミリーと1つのオペレーティングシステムに結びついています。x86-64のWindows向けにコンパイルしたプログラムは、Apple Mシリーズのチップを搭載したMacでは動きません。対象ごとに同じソースをコンパイルし直します。
コンパイラか、インタプリタか
インタプリタは、別のプログラムファイルを作らずにソースコードを直接実行します。PythonやRubyは普通この方法で実行されます。最近の言語ツールの多くは両方の考え方を使っています。CPythonはファイルをバイトコードにコンパイルしてからそれを解釈し、JVMやJavaScriptのV8エンジンは、よく使われるコードをプログラムの実行中に機械語へコンパイルします。並べて比べた表はコンパイラとインタプリタの違いのページにあります。
よくある誤解
- 「コンパイルが通れば正しく動く」 コンパイラが確認するのはプログラムが有効かどうかで、正しいかどうかではありません。論理の間違いや、ゼロ除算のような実行時の失敗はすり抜けます。
- 「Pythonはコンパイルされない」 CPythonは実行前にすべてのファイルをバイトコードにコンパイルします。
__pycache__フォルダにある.pycファイルがそのバイトコードで、プログラムがインポートするモジュールについて保存され、次回の実行ではこの段階を省けます。 - 「オンラインコンパイラはコンパイラそのもの」 ブラウザでコードを実行できるサイトは、コードをサーバーに送り、そこでツール一式(コンパイラ、リンカ、プログラム本体)を動かして出力を返します。コンパイラはその一部です。
- 「コンパイルしたコードはどこでも動く」 動くのは、ビルドの対象にしたCPUとオペレーティングシステムの上だけです。移植性は、コンパイルし直すか、JVMのような仮想マシンを使うことで得られます。
次に読むもの
Cのドキュメントでは、日常的に使われるコンパイラの姿が見られます。Cのコンパイルと実行では、実際の gcc コマンドでプリプロセス、コンパイル、アセンブル、リンクを順に確認できます。コードを実行するもう1つの方法を知るにはコンパイラとインタプリタの違いを読み、プログラムが動き出した後に周りにあるものについては実行環境を読んでください。コンパイル型の言語で練習するならCコースがおすすめです。