コンパイラとは? プログラムが機械語になるまで

約14分 · 更新: 2026年10月23日

コンパイラは、人が書いたプログラムを、CPUが実行できる0と1の命令(機械語)に、実行の前にまとめて翻訳するソフトです。翻訳の途中で書き間違いを見つけたり、同じ結果をより少ない手間で出せる形に直したり(最適化)もします。CPUは機械語しか実行できないので、こうした翻訳役が欠かせません。

この記事でわかること

  • 人が書いたプログラムを、CPUがそのまま実行できない理由
  • 1行のプログラムが機械語になるまでの段階(単語に分ける→木にする→確かめる→最適化→命令にする→つなぐ)
  • コンパイラ・インタプリタ・JITの違いと、Java・JavaScript・Pythonの場合
  • コンパイラが見つけられる誤りと、見つけられない誤り

前提知識: 特にありません。CPUを「0と1で書かれた単純な命令を、1つずつ実行する部品」と知っていると読みやすくなります。

身近なところでは

パソコンにアプリを入れるとき、受け取るファイルの中身の多くは、すでに機械語に翻訳されたプログラムです。同じゲームでも、パソコン版とスマホ版で配るファイルが別々なことがあります。プログラムを作る人の会話では、「ビルドが通った」「コンパイルエラーが出た」という言葉がよく出てきます。

こうした場面の裏で働いているのがコンパイラです。この記事では、1行のプログラムを例に、コンパイラの仕事を順に追っていきます。

なぜ「翻訳」が必要なのか

CPUが直接実行できるのは、0と1の並びで書かれた命令だけです。この形のプログラムを「機械語」と呼びます。1つの命令は「数を読み込む」「足す」「比べる」のような、とても小さな作業です。くわしくは「CPUとは?」で説明しています。

機械語は、人には読みにくく、書くのも大変です。そこで人は、英単語や数式に近い書き方でプログラムを書きます。この書き方の決まりを「プログラミング言語」と呼びます。C、Java、JavaScript、Pythonなどがあります。

人が書いたプログラムの文章を「ソースコード」と呼びます。ソースコードは文字の並びにすぎず、CPUにとっては意味がありません。そのため、機械語に直す翻訳役が必要になります。

実行する前に、プログラム全体をまとめて機械語に直すソフトを「コンパイラ」と呼びます。機械語の命令の種類と表し方は、CPUの種類ごとに違います。そのためコンパイラは、どのCPU向けの機械語にするかを決めてから翻訳します。パソコン版とスマホ版で配るファイルが違う理由の1つは、これです。

1行のプログラムが機械語になるまで

例として、時間 h を秒 s に直す、次の1行を使います。「*」は掛け算、「;」は文の終わりを表す記号です。

s = 60 * 60 * h;

ここでは話を簡単にするため、C言語に近い言語のコンパイラを例にして、段階を7つに分けます。実際のコンパイラでは、段階の分け方や名前が少しずつ違います。

1行のプログラムが、7つの段階を経て実行できるファイルになるまで ソースコード s = 60 * 60 * h; ① 単語に分ける s = 60 * 60 * h ; ② 文法を確かめ、木にする (60 × 60) × h を s へ ③ 意味を確かめる h は用意された数か? ④ 最適化 60 × 60 → 3600 ⑤ 命令の列にする h を読む 3600を掛ける s に書く ⑥ 0と1に直す 機械語(0と1の並び) ⑦ 部品とつなぐ 実行できるファイルの完成
①〜⑤がコンパイラ、⑥がアセンブラ、⑦がリンカの仕事です。⑤の命令は、日本語で書いたイメージです。省略していること: 実際の命令の書き方(CPUの種類で違う)、⑤と⑥を1つのソフトで行うコンパイラもあること、途中で使う中間の形。

① 単語に分ける(字句解析)

コンパイラから見ると、ソースコードは最初、ただの文字の列です。そこでまず、意味のあるひとかたまりごとに区切ります。「60」で1つの数、「*」で1つの記号、という具合です。この段階を字句解析と呼びます。例の行は、空白を取り除いて、8個の単語に分かれます。

② 文法を確かめて、木の形にする(構文解析)

次に、単語の並びが言語の文法に合っているかを確かめます。例えば「60 * * h」のように記号が続くと、文法の誤りとして知らせます。この段階を構文解析と呼びます。

文法に合っていれば、計算の組み立てを木の形に整理します。掛け算は左から順に計算する決まりなので、まず「60 × 60」、その結果に「× h」という形になります。一番上の「=」は、「右の計算の結果を s にしまう」という意味です。この木を構文木と呼びます。正式には「抽象構文木」(AST)といいます。

構文木の「60 × 60」の部分を、最適化で3600に置きかえる 最適化の前 最適化の後 = s × × h 60 60 = s × 3600 h
左は「s = 60 * 60 * h;」の構文木です。木の下の材料から順に計算し、最後に一番上の「=」で s にしまいます。点線で囲んだ部分は、実行する前から答えが決まっているので、右のように3600に置きかえます。省略していること: 実際の木に記録される値の種類などの情報。

③ 意味を確かめる

文法が正しくても、意味が通らないことがあります。例えば、h という名前の値の入れ物(変数)を、どこにも用意していなければ計算できません。コンパイラは、使われている名前がすべて用意されているかを確かめます。

値が数なのか文字なのかといった、値の種類を「型」と呼びます。コンパイラは、数と「abc」のような文字の並びを掛けるような、型の合わない計算がないかも確かめます。

④ もっと手間の少ない形に直す(最適化)

コンパイラは、結果を変えない範囲で、プログラムがより速く、あるいはより小さくなるように直します。これを「最適化」と呼びます。

例の「60 × 60」は、実行する前から答えが3600と決まっています。そこでコンパイラは、木のこの部分を3600に置きかえます。この最適化を定数畳み込みと呼びます。

⑤ CPUの命令の列にする(コード生成)

最適化した結果を、CPUの命令の列に直します。例の行は、「h の値を読み込む」「3600を掛ける」「結果を s の場所に書き込む」というイメージの命令になります。実際の命令の数や形は、CPUの種類によって違います。

この段階の出力は、機械語の命令を、英単語を縮めた短い記号で書いたものであることがよくあります。この書き方を「アセンブリ言語」と呼びます。

⑥ 0と1に直す(アセンブラ)

アセンブリ言語の命令を、CPUが読める0と1の並びに置きかえるソフトがアセンブラです。ふだんはコンパイラのコマンド1つで、⑥や⑦まで自動で続けて進みます。

⑦ 部品をつなぐ(リンカ)

大きなプログラムは、たくさんのファイルに分けて書きます。また、画面に文字を出すような、よく使う処理は、あらかじめ用意された部品集を使います。この部品集を「ライブラリ」と呼びます。

翻訳済みの部品どうしをつなぎ、1つの実行できるファイルにまとめるソフトがリンカです。「この処理は、あの部品のこの場所にある」という対応を埋めていきます。ライブラリを実行ファイルに入れてしまう方法と、実行するときに読み込む方法があります。違いは「静的リンクと動的リンクの違い」で表にしています。

実行するのはOSの仕事

できあがった実行ファイルは、ストレージに保存されたファイルです。パソコンやスマホでは、プログラムはメモリに置いてから実行します。アイコンをタップすると、OSがファイルの中身をメモリに読み込みます。実行中に使うライブラリも用意してから、CPUに最初の命令から実行を始めさせます。この読み込みと準備を受け持つOSの機能をローダと呼びます。OSの仕事は「OSとは?」で説明しています。

最適化はなぜ大事なのか

1回だけなら、掛け算が1つ減っても違いは感じられません。しかし多くのプログラムには、同じ部分を何百万回もくり返すところがあります。例の行が1億回くり返されるなら、定数畳み込みだけで掛け算が1億回減ります。

コンパイラは、ほかにもさまざまな最適化をします。結果に使われない計算を消す。くり返しの中で毎回同じになる計算を、くり返しの外に出す。こうした工夫を何十種類も重ねて、全体を速くします。

一方で、最適化をすると翻訳に時間がかかり、プログラムを1行ずつ止めて調べる作業もしにくくなります。そのため、C言語などで広く使われるコンパイラGCCは、指定しなければほとんど最適化しません。完成品を作るときは、「-O2」のような指定で最適化を有効にするのが一般的です。

コンパイラ・インタプリタ・JITの違い

ソースコードを、機械語のファイルに直さずに動かす方法もあります。プログラムを読みながら、その場で1つずつ実行していくソフトを「インタプリタ」と呼びます。翻訳を待たずに、書いてすぐ試せるのが利点です。その代わり、同じ部分を何度も実行するときも、そのたびに読み解く手間がかかるので、遅くなりがちです。

両方のよいところを取り入れた方式がJITコンパイルです。JITは「ジャストインタイム」(必要になったその時に)の略です。よくある形では、最初はインタプリタのように実行しながら、よく実行される部分を見つけます。その部分だけを実行中に機械語に直します。

コンパイラ・インタプリタ・JITで、機械語に直す時期の違い コンパイラ 全部を機械語に 機械語を実行 実行の前 インタプリタ 1つずつ読み解きながら実行 JIT 読みながら実行 よく使う所だけ機械語で実行 時間
横は時間の流れです。コンパイラは実行の前に訳し終えます。JITは実行の途中から、よく使う部分だけを機械語に直します。省略していること: 前もってバイトコードという中間の形に直す段階、JITで直した部分を元に戻すこと。

注意したいのは、「コンパイラ型」「インタプリタ型」が、言語ではなく、それを動かすソフトの性質だということです。実際の多くの言語は、これらを組み合わせています。

  • Java: 作る側が、ソースコードを前もって「バイトコード」という形にコンパイルして配ります。バイトコードは、特定のCPUの機械語ではなく、Java用の仮想的な機械のための命令です。実行するときは、Java用の実行ソフトがそれを解釈して動かし、よく使う部分をJITで機械語に直します。
  • JavaScript: Chromeなどで使われるV8というソフトは、ソースコードをまずバイトコードに直し、インタプリタで実行します。よく実行される部分は、最適化するコンパイラで機械語に直します。
  • Python: 標準のCPythonというソフトも、ソースコードをまずバイトコードにコンパイルし、それをインタプリタで実行します。2024年のPython 3.13では試験的なJITが加わりましたが、標準では無効です。

3つの方式は「コンパイラ・インタプリタ・JITの違い」で表にまとめています。

コンパイラが見つける誤り、見つけられない誤り

コンパイラは、実行する前に多くの誤りを見つけます。文法の誤り、用意していない名前の使用、型の合わない計算などです。誤りではないけれど怪しい書き方に、注意(警告)を出すコンパイラもあります。

一方、実行してみないとわからない誤りもあります。例えば、入力された数で割り算をするプログラムは、0が入力されると計算できません。読み込むはずのファイルがない、といった問題も、実行のときに初めてわかります。こうした実行中に起きる誤りを「実行時エラー」と呼びます。

もっとやっかいなのは、止まらずに動くのに、結果が作った人の意図と違う誤りです。「足す」つもりで「引く」と書いても、コンパイラは文法どおりに訳すだけです。こうした誤りは、実際に動かして結果を確かめる「テスト」で見つけるしかありません。

どの誤りを前もって見つけられるかは、言語によっても違います。例えばPythonでは、型の合わない計算の多くは、その行を実行したときに初めて誤りになります。

コンパイラの歴史

1950年代の初めまで、プログラムは機械語か、それに近い形で人が書いていました。1951〜1952年、グレース・ホッパーは、UNIVAC Iという計算機のために「A-0」というシステムを作りました。あらかじめ用意した計算の部品を番号で指定すると、それらをつなぎ合わせて、機械語のプログラムにするものです。

A-0は当時から「コンパイラ」と呼ばれていました。ただし、働きは今の意味のコンパイラとは違います。電気・電子分野の学会IEEEは、A-0を技術史の記念(マイルストーン)に選び、2024年に記念の銘板を設けました。その説明でも、A-0を「リンカ・ローダとして働く仕様の集まり」と書いています。

今のコンパイラの仕事の分け方や技術の土台を作ったとされるのが、IBMのジョン・バッカスたちが作ったFORTRANです。数式に近い書き方のソースコードを、IBM 704という計算機の機械語に翻訳しました。1954年の夏に始まった計画で、1957年4月に、704を使うすべての場所へ配られました。

コンパイラが作る機械語が、人が手で書いたものより大幅に遅ければ、使ってもらえません。そこでFORTRANのチームは、手書きに近い速さを目標にして、最適化に力を入れました。1982年に書かれた振り返りの論文は、その目標が驚くほど達成されたと評価しています。

ハードウェアにも「コンパイラ」に似たものがある

半導体チップの回路の設計でも、似た考え方が使われています。今の大きな回路は、図を描くのではなく、文章のような形で書くのが一般的です。回路を書くための言語をハードウェア記述言語と呼び、VerilogやVHDLがあります。

書いた回路の動きを、「かつ」「または」のような基本の論理回路の組み合わせに自動で直す作業を、論理合成と呼びます。読む、確かめる、最適化する、出力するという流れは、コンパイラとよく似ています。

大きな違いは、出力が「順に実行する命令の列」ではなく、「同時に働く回路のつながり」であることです。作った回路は、あとから書き換えられるチップに書き込んだり、専用のチップとして製造したりします。前者は「FPGAとは?」で説明しています。

よくある誤解

誤解: コンパイラで機械語にする言語は、いつも速い

実際は、コンパイルするかどうかは、速さを決める要素の1つにすぎません。計算の手順そのものが遠回りなら、どれだけ最適化しても遅いままです。JITは実行中の様子を見て最適化できるので、長く動くプログラムでは、前もって訳したものに近い速さになることもあります。また、Pythonでよく使われる計算用の部品集NumPyは、中心の計算をコンパイル済みのCのコードで行っています。同じ処理を比べればコンパイルしたほうが速いことが多いので、「いつも速い」と思われやすいのです。

誤解: コンパイラは、書いた人がしたいことを理解してくれる

実際は、コンパイラは決められた規則に従って訳すだけで、作った人の意図は知りません。エラーの表示が「ここに ; がありません」と親切に見えるのは、規則に合わない場所を報告しているからです。最適化も、言語の決まりの上で結果が変わらない範囲でしか行いません。C言語では、決まりで結果が定められていない書き方をすると、予想と違う機械語になることがあります。この書き方を未定義動作と呼びます。コンパイルが通っても、プログラムが正しいとは限りません。

誤解: Pythonはコンパイルせず、1行ずつそのまま実行している

実際は、標準のCPythonは、実行の前にファイル全体をバイトコードに直し、それをインタプリタで実行します。そのため、ファイルのどこかに文法の誤りがあると、1行目も実行されずに誤りになります。ただし、標準では機械語には直さないので、「インタプリタで動く言語」という説明も間違いではありません。「コンパイル」という言葉を、機械語に直すことだけだと思うと、この誤解が生まれます。

数字で見る

項目数字・時期メモ
A-01951〜1952年グレース・ホッパー。UNIVAC I用
FORTRANの計画の開始1954年夏IBM、ジョン・バッカスのチーム
FORTRANのコンパイラの配布1957年4月IBM 704のすべての設置先へ
FORTRANの開発の手間約18人年1人で18年かかる作業量。1957年の論文
GCCの標準の設定-O0ほとんど最適化しない。速さ重視なら -O2 など
Pythonの試験的なJIT2024年(3.13)3.13では標準で無効

FORTRANから70年近くたった今も、翻訳の段階の考え方は大きくは変わっていません。増えたのは最適化の種類と、JITのように実行中に訳す方式です。

もっと詳しく学ぶ

次に読む

シリーズ「コンピュータのしくみ」の記事

  1. コンピュータは0と1でどう計算するの? 2進数と足し算の回路
  2. CPUとは? パソコンやスマホの中で何をしているのか
  3. なぜAIにGPUが使われるの? 大量の掛け算を同時にこなすしくみ
  4. OSとは? パソコンやスマホで何をしているソフトなのか
  5. キャッシュメモリとは? CPUがメモリを待たずに済むしくみ
  6. NPUとは? スマホやPCのAI専用回路のしくみ
  7. FPGAとは? あとから回路を書き換えられるチップのしくみ
  8. 文字コードとは? 文字化けが起きる理由とUTF-8
  9. ASICとは? 特定用途向けのチップとCPU・FPGAとの違い
  10. コンパイラとは? プログラムが機械語になるまで
  11. 論理ゲートとは? AND・OR・NOTで計算できるしくみ
  12. RISC-Vとは? 誰でも使える命令セットのしくみ
  13. アルゴリズムとは? 手順の工夫で計算が速くなるしくみ
  14. データベースとは? 表・SQL・インデックスのしくみ
  15. Gitとは? バージョン管理のしくみとGitHubとの違い

この分野の問題: コンパイラ

参考にした資料

やさしい解説の一覧へ