さっきの記事を投稿したのはいいが、肝心の「いろは」の部分を記述していなかった。
かれこれ5年以上前の知識で大半は忘れてしまったが、それでも覚えている部分は多分重要な部分だからだろう、ということで備忘録として残すことにした。
コンパイルとは
某有名落ちものパズルゲーを作った会社のことではないw。
ソースコードから実行できるファイルを生成することを指す、こっちのことだ。
gcc sample.c -o sample
でソースコードsample.c
から実行ファイルsample
を生成する、この行為のことだが、その過程は大きく以下の3つに分けられている。
- 前処理(プリプロセス)
- オブジェクトファイル生成(コンパイル)
- リンク
gcc
だと -E
-c
のオプションで段階的に処理結果を確認できる。
前処理だけ行う
gcc -E sample.c
マクロ関数(#include
や#ifdef
なども)はこれで展開されて中身を確認できる。
自分はしないように気をつけていても、何かの事情でマクロ関数だけで画面2スクロールさせるくらい処理を書いてる変人がいることもある。
そんな過去の人が書いたコードを数十年保守するのが新人Cプログラマーの役目だと悟った時の絶望は今でも忘れない。
その時だけのマクロ定義
-D
オプションを指定すると、そのコンパイル時にだけ有効になるマクロを定義することができる。
例えば、隠し機能をコードに埋めておいて、デバッグ用のコンパイル時にだけ有効にする、みたいな使い方ができる。
オブジェクトファイルの生成
gcc -c sample.c
コンパイルのメイン処理。オブジェクトファイルを生成する。最適化やデバッグオプションを指定するならここで指定する。
デバッガオプション
gdb
を利用するときに-g
指定する。実はレベルを指定できて-g3
はマクロ関数の呼び出しが可能なデバッガオプション。
デバッガを利用するときは最適化なし(-O0
)を合わせて指定するのがいい。
gcc -O0 -g3 sample.c
最適化オプション
何を優先で最適化するかによりコマンドが違うので、詳細はmanコマンドで要確認。
デバッガを使うときは、最適化しない-O0
を指定。
リンク
gcc sample.o
コンパイルしたオブジェクトファイルをリンクして実行ファイルにする。
math.h
など別のライブラリにある関数を利用する場合は-lm
などのオプションを指定する。
ライブラリの生成
ライブラリの生成には-shared
-fPIC
のオプションを指定する。
/lib
, /usr/lib
とかにある共有ライブラリ lib*.so
ファイルを作成することができる。
-fPIC
は動的リンクするライブラリにしたいときに指定する。
出力する共有ライブラリは必ず lib〜.so ファイル名にする。(先頭のlibがないと利用できないので注意)
gcc -shared infile1.c infile2.c ... -o liboutfile.so
ライブラリのリンク
ライブラリをリンクするときは、-L
でライブラリファイルのある ディレクトリを 指定する。(環境変数LIBRARY_PATH
に追加してもいい)
同様に、ライブラリのヘッダファイルは-I
でヘッダファイルのある ディレクトリを 指定する。
また、ライブラリ名がliboutfile.so
の場合は、-l
オプションで-loutfile
と指定する。
lib〜.so
の〜
部分を指定するのがポイント。
gcc sample.o -I./headerdirname -L./libdirname -loutfile
実行するときはLD_LIBRARY_PATH
リンク時はLIBRARY_PATH
にライブラリのディレクトリを指定するが、実行時にライブラリが見つからずエラーになることがある。
そんなときは配置したディレクトリを環境変数 LD_LIBRARY_PATH
に追加する。
経験上「コンパイルできるのに動かない」ときは、だいたいこれか、リンク時のオプション忘れが原因。
知っておいたほうが便利なコマンド
どのライブラリがリンクできていないかを知りたい
ldd
コマンドで確認できる。
ldd liboutfile.so
not found
になっていたらLD_LIBRARY_PATH
に追加できていない。
共有ライブラリの中から別の共有ライブラリを参照することもあるので、再帰的に確認したほうがいい。
このライブラリがあるディレクトリを知りたい
locate
コマンドが便利。
locate libm.so
たまにupdatedb
が実行されていなくて期待を裏切られることもある。
(おまえのことだぞ、cygwin)
そんなライブラリないんだけど
そもそも必要なライブラリがインストールされていない可能性も考慮するならググったほうがいい。
Ubuntuの場合は apt-file
というパッケージが存在するので、ライブラリファイル名から必要なパッケージが検索できる。
このライブラリが提供する関数を知りたい
共有ライブラリの中に存在する関数を確認するときはnm
コマンドが便利。
nm liboutfile.so
この命名規則に従っていない関数作ったやつ誰だ。このライブラリはあそこの部署か、またか、みたいな犯人探しができるw