C言語入門|コンパイラとリンカの役割

プログラムは一気に完成しているわけではない

C言語のプログラムは、ソースコードを書いてコンパイルすると、すぐに実行できるファイルができあがるように見えます。
しかし実際には、その裏側で複数の工程が順番に進められています。

その工程を支えているのが、

  • コンパイラ
  • リンカ

という、役割の異なるソフトウェアです。

ここでは、プリプロセッサの後に何が起きているのか を、順を追って見ていきましょう。

プログラムが完成するまでの全体像

C言語のプログラムは、次の流れで完成します。

まず、私たちが書いたソースコードは、プリプロセッサによって前処理されます。
この段階で、コメントの削除や include の展開、マクロの置換が行われます。

次に、その前処理済みのソースコードが コンパイラ に渡されます。
コンパイラは、C言語の文法として正しいかを確認し、問題がなければ CPU が理解できるマシン語へと翻訳します。
この結果として作られるのが オブジェクトファイル です。

ただし、オブジェクトファイルはまだ完成品ではありません。
printf のような標準関数や、別ファイルで定義された関数の中身は含まれていないため、そのままでは実行できません。

そこで最後に登場するのが リンカ です。
リンカは、複数のオブジェクトファイルやライブラリファイルを結合し、必要な関数の実体をすべてそろえたうえで、最終的な 実行可能ファイル を作成します。

このように、
コンパイルとリンクは別の工程 として行われているのです。

コンパイラの役割とは何か

プリプロセッサを通過したソースコードは、まだ人間にも読めるテキストです。
しかし、CPU が理解できるのはマシン語だけでしたね。

このギャップを埋めるのがコンパイラです。

コンパイラが行う2つの仕事

コンパイラの仕事は、次の2つに整理できます。

項目内容
文法チェックC言語のルールに沿って書かれているか確認する。
翻訳ソースコードをマシン語へ変換する。

この結果として生成されるのが オブジェクトファイル です。

オブジェクトファイルの正体

オブジェクトファイルは、

  • マシン語に変換されている。
  • 文法エラーは存在しない。

という状態ですが、

  • 単体では実行できない。

という特徴を持っています。

理由は、他のファイルに定義された関数や、標準ライブラリの関数の実体がまだ含まれていないからです。

コンパイルが通っても実行できない理由

たとえば、printf を使ったプログラムを考えてみましょう。

stdio.h をインクルードしているため、printf を使うことはできます。
しかし、インクルードによって取り込まれているのは printf の宣言 だけです。

printf が実際にどのような処理を行う関数なのか、その中身は 別の場所 にあります。

その不足分を補う工程が必要になります。

リンカの役割とは何か

リンカは、未完成の部品であるオブジェクトファイルを集めて、
1つの完成品に仕上げる役割を担います。

リンカの仕事

リンカの仕事をまとめると、次のようになります。

項目内容
結合複数のオブジェクトファイルをまとめる。
解決関数や変数の参照先を確定させる。
生成実行可能ファイルを作成する。

このとき、必ず main 関数を含むオブジェクトファイル が必要になります。

標準ライブラリと libc の関係

printf をはじめとする標準関数の実体は、
libc というライブラリにまとめられています。

この libc もリンカによって結合される対象の1つです。

C言語の開発では、ほぼすべてのプログラムが libc を利用するため、通常は特別な指定をしなくても自動的にリンクされます。

ライブラリファイルの種類

ライブラリには、用途に応じていくつかの形式があります。

拡張子内容
.o / .obj単体のオブジェクトファイル
.a / .lib静的ライブラリ
.so / .dll動的リンクライブラリ

動的リンクライブラリは、実行時に読み込まれるため、実行ファイルのサイズを小さくできるという特徴があります。

コンパイラとリンカの役割まとめ

最後に、役割分担を整理しておきましょう。

担当役割
コンパイラ文法チェックとマシン語への翻訳
リンカ部品の結合と実行可能ファイルの完成

この分業構造があるからこそ、

  • ファイル分割
  • 複数人開発
  • ライブラリの再利用

が可能になっています。