gccでCソースをコンパイルする
Cのソースコードは、そのままでは実行できません。コンパイラ gcc に渡して、CPUが解釈できる機械語の実行ファイルへ変換します。最も単純には gcc hello.c とだけ打つと、a.out という名前の実行ファイルが生成されます。-o オプションで出力名を指定すれば、gcc hello.c -o hello のように好きな名前の実行ファイルを作れます。生成した実行ファイルは ./hello のようにパスを付けて起動します。
C言語で書いたプログラムは、ファイルに保存しただけでは動かない。中身はただの英単語と記号の集まりで、CPUはそれをそのまま実行できないからだ。
CPUが直接理解できるのは、機械語と呼ばれる0と1の命令列だけである。だから、人が書いたソースコードを機械語へ翻訳する誰かが要る。
この翻訳がコンパイルで、翻訳を担うプログラムをコンパイラと呼ぶ。Linux環境で最も広く使われるCコンパイラが gcc(GNU Compiler Collection)だ。
gcc にソースファイルを渡すと、その内容を解析して機械語に変換し、そのまま起動できる実行ファイルを生成する。Cでは「書く→コンパイルする→動かす」という3拍子が基本のリズムになる。
🔨 いちばん単純なコンパイル
例として、画面に文字を表示するだけの hello.c があるとする。中身は #include <stdio.h> から始まり、main 関数の中で printf を呼ぶだけの数行のプログラムだ。
これをコンパイルする最も短い書き方は、ターミナルで gcc hello.c と打つことである。
ここで一瞬、不安になるかもしれない。エラーがなければ、gcc は何もメッセージを出さずに終わるからだ。
だがこれは失敗ではない。Linuxのコマンドは成功すると黙るのが流儀なので、「何も出ないのは失敗ではなく成功」と覚えておこう。
このとき、カレントディレクトリには a.out という名前のファイルができている。これが翻訳の成果物である実行ファイルで、リンクまで終えて生成される最終成果物だ。
この a.out という名前は、assembler output(アセンブラの出力)に由来する歴史的な既定名である。出力名を指定しなかったときに gcc が使う名前だ。
ls で確認すると、ソースの hello.c と並んで a.out が増えているのが見える。a.out は実行可能な状態になっていて、ファイルには実行権限(x)が付いている。
🏷 出力名を指定する -o オプション
毎回 a.out という名前ができると、複数のプログラムを作ったとき、どれがどれだか分からなくなる。
この困りごとを解くのが -o オプションだ。-o を使えば、出力する実行ファイルの名前を自分で指定できる。
たとえば gcc hello.c -o hello と打てば、a.out ではなく hello という名前の実行ファイルが作られる。-o は output の頭文字で、その直後に書いた名前が出力ファイル名になる。
順序を気にする必要はない。オプションとソースファイルの順序は自由で、gcc -o hello hello.c と書いても結果は同じだ。
実務ではほぼ必ずこの -o を付けて、ソース名と対応した分かりやすい名前を付けるのが習慣になっている。
▶ できた実行ファイルの動かし方
出力名を付けて満足したのも束の間、いざ動かそうとすると最初の壁にぶつかる。生成した実行ファイルは、ファイル名をそのまま打つだけでは起動できないことがあるのだ。
hello とだけ打つと「コマンドが見つかりません(command not found)」と言われがちである。
理由はこうだ。シェルは、コマンドを決まった検索パス(環境変数 PATH に並んだディレクトリ群)の中からしか探さず、カレントディレクトリは原則その対象に含めない。
これはセキュリティ上の配慮である。いまいる場所にたまたま置かれた紛らわしい名前のプログラムを、うっかり実行してしまわないようにする仕組みだ。
そこで ./hello のように、先頭に ./ を付けて「いまいるディレクトリのこのファイル」と明示的に指定する。
. は現在地を表す記号なので、./hello は「カレントディレクトリにある hello を実行せよ」という意味になる。実行すると、プログラムが動いて結果が画面に表示される。
自分で作ったコマンドをどこからでも呼べるようにしたいときは、置き場所を PATH に加える、という発展的なやり方もある。
⚠ つまずいたら、まずここ
初学者がつまずきやすいのは、まず ./ の付け忘れだ。hello だけ打って command not found が出たら、ほぼこれが原因である。
次に多いのが、ソースに文法ミスがあってコンパイル自体が通らないケースだ。セミコロンの付け忘れやかっこの対応ミスがあると、gcc は error: と書かれたメッセージを行番号付きで出して止まる。
このときは実行ファイルは作られないので、慌てず指摘された行を直して再度コンパイルすればよい。
また、printf などを使うのに必要な宣言が無いと warning(警告)が出ることがある。これらの宣言はヘッダファイル(.h)を #include で取り込むが、その仕組みは後の段階で詳しく扱う。
ここで一つ気をつけたい。warning はエラーと違ってコンパイル自体は止まらず実行ファイルもできてしまうため、つい見過ごしがちだ。
だが warning は潜在的な不具合の予告であることが多い。出たらできるだけその場で解消する習慣をつけよう。
なお、コンパイルが通っても実行ファイルに実行権限が無いと動かせないことがあり、その場合は権限を付与する必要がある。
これらのつまずきは、4段階の工程やヘッダとライブラリの関係を学ぶと、なぜ起きるのかが腑に落ちるようになる。
🌱 この一打が土台になる
gcc によるコンパイルは、C/C++を扱うあらゆる開発の出発点だ。
組込みLinuxの世界でも、デバイスを制御する小さなツールから、カーネルモジュール、各種ライブラリまで、土台はこのコンパイルの上に成り立っている。
最初は gcc ソース -o 名前 で実行ファイルを作り、./名前 で動かす。この一往復を、手を動かして体に覚え込ませることが何より大切だ。
ソースが1つのうちはこの単純な形で十分だが、ファイルが増えてくると、後述する4段階の理解や Makefile による自動化が効いてくる。
まずは「翻訳して、できた実行ファイルを起動する」という最小の流れを、確実に再現できるようにしておこう。その単純な一打の裏で何が起きているのかは、次のトピックで内部をのぞく。