Huney

応用情報(AP) / ソフトウェア

言語処理ツールとは?コンパイラ・最適化・リンクを基礎から理解しよう

プログラムを実行可能な形式へ変換する言語処理ツールについて、言語プロセッサ、コンパイラ、コンパイル技法、最適化、リンク、ビルドの自動化まで初心者向けに解説します。

読了時間:約13分
  • #ソフトウェア
  • #言語処理ツール
  • #コンパイラ
  • #インタプリタ
  • #アセンブラ
  • #リンカ
  • #ビルドツール
  • #応用情報技術者試験

言語処理ツールとは?コンパイラ・最適化・リンクを基礎から理解しよう

はじめに

私たちはプログラムを書くとき、

int x = 10;
int y = 20;
printf("%d", x + y);

のようなプログラミング言語を利用します。

しかし、CPUがこのソースコードをそのまま理解して実行しているわけではありません。

CPUが直接扱えるのは、最終的には機械語です。

そこで、

ソースプログラム
       ↓
言語処理
       ↓
機械が実行できる形式
       ↓
実行

という変換が必要になります。

このような処理を行うソフトウェアが言語プロセッサです。


1. 言語プロセッサとは

**言語プロセッサ(Language Processor)**とは、

プログラミング言語などで記述されたプログラムを、コンピュータで処理できる形式へ変換・実行するソフトウェア

です。

代表的なものには、

  • コンパイラ
  • インタプリタ
  • アセンブラ

などがあります。


2. コンパイラ

**コンパイラ(Compiler)**は、

ソースプログラムをまとめて翻訳し、機械語などの目的プログラムへ変換する言語プロセッサ

です。

基本的な流れは、

ソースプログラム
       ↓
   コンパイラ
       ↓
目的プログラム

となります。

CやC++などで代表的に利用される方式です。

翻訳処理を事前に行っておくため、生成されたプログラムは高速に実行しやすいという特徴があります。


3. インタプリタ

**インタプリタ(Interpreter)**は、

プログラムを実行しながら逐次解釈して処理する言語プロセッサ

です。

イメージとしては、

ソースコード
    ↓
1つずつ解釈
    ↓
実行
    ↓
次を解釈
    ↓
実行

という形です。

コンパイラのように、あらかじめプログラム全体を機械語へ変換してから実行する方式とは考え方が異なります。


4. コンパイラとインタプリタ

基本的な違いを整理すると、

項目 コンパイラ インタプリタ
基本的な処理 事前に翻訳 実行時に逐次解釈
目的プログラム 生成する方式が代表的 原則として直接実行
実行速度 高速にしやすい 翻訳処理が実行時にも必要
エラー検出 コンパイル時に検出 実行過程で検出されるものもある

ただし、現在のプログラミング言語では、コンパイルと実行時処理を組み合わせた方式も多く存在します。


5. アセンブラ

**アセンブラ(Assembler)**は、

アセンブリ言語で書かれたプログラムを機械語へ変換する言語プロセッサ

です。

例えば、

アセンブリ言語
      ↓
  アセンブラ
      ↓
   機械語

という変換を行います。

アセンブリ言語は機械語に近い低水準言語です。


6. その他の言語プロセッサ

言語処理では、コンパイラ・インタプリタ・アセンブラ以外にもさまざまなツールがあります。

代表的なものを見ていきましょう。


7. プリプロセッサ

**プリプロセッサ(Preprocessor)**は、

コンパイルを行う前にソースプログラムを前処理するプログラム

です。

例えばC言語では、

#include <stdio.h>
#define MAX 100

などの記述を処理します。

流れとしては、

ソースコード
     ↓
プリプロセッサ
     ↓
前処理済みコード
     ↓
コンパイラ

となります。


8. トランスレータ

広い意味での**トランスレータ(Translator)**は、

あるプログラミング言語で書かれたプログラムを別の言語や形式へ変換するもの

です。

例えば、

言語A
 ↓
トランスレータ
 ↓
言語B

のような変換を行います。

コンパイラやアセンブラも、広い意味では翻訳プログラムの一種として捉えることができます。


9. ジェネレータ

**ジェネレータ(Generator)**は、

必要な条件やパラメータなどを指定することで、プログラムなどを自動生成するツール

です。

人間がすべてのソースコードを一から記述するのではなく、一定の規則に基づいてコードなどを生成することで開発を効率化できます。


10. シミュレータ

**シミュレータ(Simulator)**は、

実際のシステムやコンピュータなどの動作を、別の環境上で模擬するためのソフトウェア

です。

例えば、

実際のシステム
      ↓
動作をモデル化
      ↓
PC上で再現

することで、

  • システムの動作確認
  • 性能評価
  • テスト
  • 教育・訓練

などに利用できます。

実際の機器を用意しなくても、想定した条件で動作を確認できることが特徴です。


11. コンパイルとは

**コンパイル(Compile)**とは、

ソースプログラムを機械語などの目的プログラムへ翻訳する処理

です。

例えば、

main.c
  ↓
コンパイル
  ↓
main.o

のように、ソースコードから**オブジェクトファイル(目的プログラム)**を生成します。

ただし、オブジェクトファイルが生成された時点では、必ずしもそのまま実行できるとは限りません。

複数のプログラムやライブラリを組み合わせる必要があるからです。

そこで登場するのがリンクです。


12. コンパイラの処理手順

コンパイラ内部では、単純にソースコードを一気に機械語へ変換しているわけではありません。

代表的には、

ソースプログラム
      ↓
① 字句解析
      ↓
② 構文解析
      ↓
③ 意味解析
      ↓
④ 中間コード生成
      ↓
⑤ 最適化
      ↓
⑥ コード生成
      ↓
目的プログラム

という流れで処理します。

ここは応用情報でも重要です。


13. 字句解析

**字句解析(Lexical Analysis)**では、

ソースコードを意味のある最小単位であるトークンへ分解する

処理を行います。

例えば、

x = a + 10;

というコードなら、

x
=
a
+
10
;

などの単位へ分解します。

これらを**トークン(Token)**と呼びます。


14. 構文解析

**構文解析(Syntax Analysis)**では、

トークンの並びがプログラミング言語の文法に従っているか確認する

処理を行います。

例えば、

x = a + 10;

なら正しい文法でも、

x = + a 10;

のような記述では文法上の問題が発生します。

構文解析では、プログラムの構造を表す構文木などを作成して処理します。


15. 意味解析

文法的に正しくても、意味として正しいとは限りません。

そこで行われるのが**意味解析(Semantic Analysis)**です。

例えば、

数値 + 数値

なら問題ありませんが、言語や型によっては、

数値 + 不適切な型のデータ

が許されない場合があります。

意味解析では、

  • 型が適切か
  • 変数が正しく宣言されているか
  • 演算が正しく行えるか

などを確認します。


16. 中間コード生成

解析したプログラムから、

中間コード(Intermediate Code)

を生成する場合があります。

ソースコード
     ↓
中間コード
     ↓
機械語

と一段階挟むことで、最適化や異なるCPU向けのコード生成などを行いやすくなります。


17. コンパイラの最適化

コンパイラは、プログラムの意味を変えない範囲で、

より高速に実行できる、または効率のよいコードへ変換する

ことがあります。

これを**最適化(Optimization)**と呼びます。


18. 定数畳み込み

例えば、

x = 10 * 20;

という処理があったとします。

実行時に毎回計算するのではなく、コンパイル時に計算して、

x = 200;

のように変換できます。

これを**定数畳み込み(Constant Folding)**と呼びます。


19. 定数伝播

例えば、

x = 10;
y = x + 5;

のように、変数xの値が10だと分かっている場合、

y = 10 + 5;

のように定数を利用して処理できます。

これを**定数伝播(Constant Propagation)**と呼びます。


20. 共通部分式の除去

例えば、

x = a * b + 10;
y = a * b + 20;

では、

a * b

という同じ計算が2回登場しています。

そこで、

temp = a * b;
x = temp + 10;
y = temp + 20;

のように、一度計算した結果を再利用できます。

これを共通部分式の除去と呼びます。


21. 不要コードの除去

実行結果に影響しない不要な処理を削除する最適化もあります。

例えば、

x = 10;
x = 20;
printf("%d", x);

で、最初の、

x = 10;

がその後一度も利用されないのであれば、削除できる場合があります。

このような処理を**不要コード除去(Dead Code Elimination)**などと呼びます。


22. ループ最適化

プログラムでは、同じ処理を繰り返すループが多く使われます。

そのため、

ループ内で何度も実行される不要な計算を減らす

ことは性能向上につながります。

例えば、

for (...) {
    x = 10 * 20;
    ...
}

の 10 * 20 のように、ループ中に結果が変化しない計算であれば、ループの外へ移動できる場合があります。

このような処理をループ不変式の移動と呼びます。


23. 最適化を整理

代表的な最適化をまとめると、

最適化 基本的な考え方
定数畳み込み 定数の計算をコンパイル時に済ませる
定数伝播 確定している定数を後続処理へ反映する
共通部分式除去 同じ計算を繰り返さない
不要コード除去 結果に影響しない処理を削除する
ループ最適化 繰り返し処理の無駄を減らす

最適化では、

プログラムの意味・結果を変えずに効率を改善する

ことが基本です。


24. リンクとは

ソースコードをコンパイルしただけでは、プログラムが完成しない場合があります。

例えば、

main.c
calc.c

という2つのソースファイルがあれば、

main.c → main.o
calc.c → calc.o

のように、それぞれコンパイルできます。

しかし、最終的にはこれらを1つの実行可能なプログラムとしてまとめる必要があります。

この処理が、

リンク(Link)

です。


25. リンカ

リンクを行うプログラムを、

リンカ(Linker)

と呼びます。

main.o ──┐
         │
calc.o ──┼→ リンカ → 実行可能ファイル
         │
ライブラリ ─┘

リンカは、

  • 複数のオブジェクトファイル
  • 必要なライブラリ

などを結合し、プログラム中の参照関係を解決します。


26. 静的リンク

**静的リンク(Static Linking)**では、

必要なライブラリのコードを、リンク時に実行可能ファイルへ組み込む

方式です。

プログラム
+
ライブラリ
    ↓
実行可能ファイルにまとめる

必要なコードが実行可能ファイルに含まれるため、ファイルサイズが大きくなりやすいという特徴があります。


27. 動的リンク

**動的リンク(Dynamic Linking)**では、

ライブラリを実行可能ファイルへすべて組み込まず、実行時などに共有ライブラリを利用する

方式です。

実行可能ファイル
      ↓
共有ライブラリを利用

複数のプログラムから同じライブラリを共有できるため、ディスクやメモリを効率的に利用しやすくなります。


28. コンパイルから実行まで

ここまでの流れをつなげてみましょう。

ソースプログラム
      ↓
プリプロセッサ
      ↓
コンパイラ
      ↓
オブジェクトファイル
      ↓
リンカ
      ↑
ライブラリ
      ↓
実行可能ファイル
      ↓
実行

つまり、

コンパイル
→ ソースコードを翻訳する

リンク
→ オブジェクトファイルやライブラリを結合する

という違いです。

🍯 「コンパイルしたら完成」ではなく、「コンパイル → リンク → 実行」と覚えておきましょう。


29. コンパイル・リンクの自動化

実際の開発では、ソースファイルが数個ではなく、

main.c
user.c
network.c
database.c
file.c
...

のように大量に存在することがあります。

これを毎回手作業で、

これをコンパイル
↓
次もコンパイル
↓
最後にリンク

と操作するのは大変です。

そこで利用されるのがビルドツールです。


30. ビルドツール

**ビルド(Build)**とは、

ソースコードなどから実行可能なプログラムを作成する一連の処理

です。

ビルドには、

  • 前処理
  • コンパイル
  • リンク

などが含まれます。

これらを自動化するのがビルドツールです。

代表的なものとして、

make

があります。


31. make

makeは、

プログラムのコンパイルやリンクなどを自動化するための代表的なビルドツール

です。

通常、どのファイルから何を生成するのかといったルールをMakefileへ記述します。

例えば、

main.c ─→ main.o ─┐
                  ├→ app
calc.c ─→ calc.o ─┘

という依存関係を管理できます。

さらに、変更されたファイルに関係する部分だけを再コンパイルすることで、大規模なプログラムでも効率よくビルドできます。


32. 言語処理の全体像

最後に、今回の内容をまとめます。

ソースプログラム
      ↓
プリプロセッサ
      ↓
コンパイラ
      │
      ├─ 字句解析
      ├─ 構文解析
      ├─ 意味解析
      ├─ 中間コード生成
      ├─ 最適化
      └─ コード生成
      ↓
オブジェクトファイル
      ↓
リンカ ← ライブラリ
      ↓
実行可能ファイル
      ↓
実行

そして、この一連の作業を、

makeなどのビルドツール
        ↓
コンパイル・リンクを自動化

できます。


まとめ

この記事で覚えること

  • 言語プロセッサはプログラムを翻訳・実行するためのソフトウェア
  • コンパイラはソースプログラムをまとめて目的プログラムへ翻訳する
  • インタプリタはプログラムを実行しながら逐次解釈する
  • アセンブラはアセンブリ言語を機械語へ変換する
  • シミュレータは実際のシステムなどの動作を模擬する
  • コンパイラでは字句解析・構文解析・意味解析・中間コード生成・最適化・コード生成などを行う
  • コンパイラはプログラムの意味を変えない範囲でコードを最適化する
  • リンクはオブジェクトファイルやライブラリなどを結合する処理
  • 静的リンクと動的リンクではライブラリの利用方法が異なる
  • ビルドはソースコードから実行可能なプログラムを作る一連の処理
  • makeなどを利用してコンパイルやリンクを自動化できる

🍯 はちみつメモ

プログラム完成までの大きな流れは「ソースコード → コンパイル → オブジェクトファイル → リンク → 実行可能ファイル」。コンパイラの中では「字句 → 構文 → 意味 → 中間コード → 最適化 → コード生成」の順番を押さえておこう!