C は中級言語であり、プログラムをマシン上で実行できるように実行コードに変換するためのコンパイラが必要です。
どのようにしてCプログラムをコンパイルし、実行するのでしょうか。
以下に、gccコンパイラを搭載したUbuntuマシンでの手順を示します。
- まず、エディタを使ってC言語のプログラムを作成し、ファイル名をfilename.cとして保存します
$ vi filename.c
- 右の図は、2つの数字を足す簡単なプログラムを示しています。
- それを以下のコマンドでコンパイルします。
$ gcc –Wall filename.c –o filename
- オプションの-Wallは、コンパイラのすべての警告メッセージを有効にします。 このオプションは、より良いコードを生成するために推奨されます。
オプション -o は、出力ファイル名を指定するために使用します。
- コンパイル後に実行ファイルが生成されるので、以下のコマンドで実行します。
$ ./filename
コンパイルプロセスでは何が行われるのでしょうか?
コンパイラはCプログラムを実行ファイルに変換します。 Cプログラムが実行ファイルになるまでには4つの段階があります。
- 前処理
- コンパイル
- アセンブル
- リンク
以下のコマンドを実行すると、カレントディレクトリにあるすべての中間ファイルと実行ファイルが得られます。
$gcc –Wall –save-temps filename.c –o filename
以下のスクリーンショットは、生成されたすべての中間ファイルを示しています。
これらの中間ファイルに何が含まれているか、一つずつ見ていきましょう。
前処理
ソースコードが渡される最初のフェーズです。 このフェーズには以下が含まれます。
- コメントの削除
- マクロの展開
- インクルードファイルの展開
- 条件付きコンパイル
前処理された出力は、ファイル名に格納されます。i. filename.iの中身を見てみましょう: using $vi filename.i
上の出力では、ソースファイルはたくさんの情報で埋め尽くされていますが、最後には私たちのコードが保存されています。
分析してみましょう。
- printf が add(a, b) ではなく、a + b になっているのは、マクロが拡張されたからです。
- コメントが削除されています。
- #include<stdio.h> がない代わりに、たくさんのコードが表示されています。
コンパイル
次のステップでは、filename.iをコンパイルして、中間コンパイル出力ファイルfilename.sを作成します。このファイルは、アセンブリレベルの命令です。
スナップショットでは、アセンブラが理解できるアセンブリ言語であることがわかります。
アセンブリ
このフェーズでは、filename.sが入力として扱われ、アセンブラによってfilename.oに変換されます。 このファイルには機械レベルの命令が含まれています。 この段階では、既存のコードのみが機械語に変換され、printf()のような関数呼び出しは解決されません。 このファイルを $vi filename.o で見てみましょう
リンク
この段階では、関数呼び出しとその定義のリンクがすべて行われます。 リンカは、これらすべての関数がどこに実装されているかを知っています。 リンカは余分な仕事もします。プログラムの開始と終了の際に必要となる余分なコードをプログラムに追加します。 例えば、コマンドライン引数を渡すような環境設定に必要なコードがあります。 この作業は、$size filename.oと$size filename.oを使うことで簡単に確認できます。 これらのコマンドを通じて、出力ファイルがどのようにオブジェクトファイルから実行ファイルに増加するかがわかります。 これは、リンカーが私たちのプログラムに追加した余分なコードのためです。
p