UNIX/Linuxの部屋 gccコマンドの使い方

TOP UNIX/Linuxの部屋 UNIX/Linuxコマンド一覧 用語集 新版 由来/読み方辞書 環境変数マニュアル Cシェル変数 システム設定ファイル システムコール・ライブラリ ネットワークプログラミングの基礎知識 クラウドサービス徹底比較・徹底解説




コマンド gcc GNU コンパイラコレクション。C・C++ 等のソースを実行形式のバイナリに変換する このエントリーをはてなブックマークに追加

最終更新


GCC は GNU が作成している C・C++ 等のコンパイラである。Linux においては標準の C コンパイラが gcc になっている (cc と gcc へのシンボリックリンクになっている)。Solaris など商用マシンでも gcc の重要度は高く、OS 標準のコンパイラではコンパイルできず、gcc を使わないといけないソフトウェアも多い。



gcc コマンドの基本的な使い方
エディタで適当なファイル、例えば sample.c に
#include <stdio.h>
main(){
printf("hello world.\n");
}
と書いて、
% gcc sample.c
とすると、a.out というコンパイル済のバイナリファイルが作成される。実行すると
% ./a.out
hello world.
となる。*.cc という拡張子は C++ のソースとして扱われる。

gcc コマンドの内部動作
gcc の内部では下記が順に呼ばれる。
1. プリプロセッサ (/usr/bin/cpp や /usr/libexec/cpp)
前処理用プログラム。#include、#define などを処理。
2. コンパイラ (/usr/libexec/cc1 など)
C 言語をアセンブラに変換。
3. アセンブラ (/usr/bin/as)
アセンブラをマシン語に変換。出力はオブジェクトファイル(*.o)。
4. リンカ (/usr/bin/ld)
オブジェクトファイルとライブラリをリンク (結合処理) する。
-E、-S、-c オプションを使うと任意の段階で処理を止めることができる。

gcc コマンドのオプション
-E プリプロセス後終了。コンパイルをしない。
プリプロセッサの出力を標準出力に書き出す。#include や #define などがプリプロセッサによって処理される。#ifdef 〜 #endif が多くて読みにくいソースは -E オプションを使うとよい。なお、インクルードファイルの読み込み部分で多くの空行が挿入されるので、cat コマンドの -s オプション (連続する空行を 1行にまとめる) を使って
% gcc -E foo.c | cat -s | less
などとするのがお勧め。
-S コンパイル後終了。アセンブルをしない。
-c アセンブル後終了。リンクを実行しない。
*.o というオブジェクトファイルを生成した時点でストップする。

-g デバッグ用の情報を保存する
gdb などのデバッガを使う場合に有用な情報をバイナリに残しておく。
-static 静的にリンクする
通常、printf(3) を使用しているプログラムをコンパイルすると、
% gcc a.c
% ldd ./a.out
./a.out:
libc.so.4 => /usr/lib/libc.so.4 (0x28066000)
というふうに、実行時に共有ライブラリを動的にリンクするようなバイナリが生成される。この例では a.out は共有ライブラリ libc.so.4 を使用するようになっており、実行時に libc.so.4 が存在しないと a.out は実行できない。

一方、-static オプションを付けてリンクすると、必要なライブラリがバイナリにリンクされ、バイナリ単体で実行が可能になる。
% gcc a.c
% ldd a.out
ldd: a.out: not a dynamic executable

ただし、静的リンクをすると以下の欠点があることを把握しておこう。
  • バイナリサイズが増加する
  • 複数のプロセス間で実行時にライブラリのメモリを共有できない(共有ライブラリにしておけば、テキスト領域の分だけメモリを共有できる)
  • ライブラリにバグがあったら、全バイナリをコンパイルしなおす必要がある
-o 生成する実行ファイル名を指定。-o で指定しないと a.out というバイナリが生成される。
% gcc -o bar foo.c
⇒ 生成されるバイナリは bar となる。
% gcc foo.c
⇒ デフォルトのファイル名である、a.out が生成される。
-v バーボーズモード。
マクロ、インクルードパス、プリプロセッサ、アセンブラ、リンカに渡すオプションを全て表示する。
-pipe テンポラリファイルを作らず、パイプで処理する。
コンパイルにかかる時間が短縮されるが、より多量のメモリが必要。
-ansi ANSI C に準拠する。
GNU C 独自の拡張機能である asm・inline・typeof などが使えなくなる。
-trigraphs ANSI C のトライグラフを認識する
-traditional 伝統的な C の文法を認識する
-w 警告 (warning) を表示しない
-W... 各種の警告 (warning) を表示するかどうか設定する
例えば -Wimplicit-function-declaration や -Wswitch など、数十種類の -W... オプションがある。全ての -W... を有効にするには -Wall を使う。
-Wall -Wスイッチで指定できる全ての警告 (warning) を表示する
なるべく -Wall オプションを付けても警告 (warning) が出ないようなコードを書くべき。
-Wl, リンカに渡すオプションを定義する。
非常に分かりづらいが、警告系の -W オプションとは無関係。例えば
% gcc -Wl,-rpath=. hoge.c
とすると、リンカに「-rpath=.」というオプションが渡る。
-O 最適化したコードを生成する。
実行速度が速くなったり、バイナリのサイズを減らすことができる。-O2 -O3 などと数字を与えることで、最適化の段階を指定することができる。数字が大きいほど賢い最適化を行う。最適化を行うとコンパイルに時間がかかるので、デバッグ終了後に行うとよい。
-D マクロを定義
% gcc -DHOGE foo.c
とするのは、foo.c の先頭に
#define HOGE 1
と書いたことと同じである。また、
% gcc -DHOGE=10 foo.c
% gcc -DHOGE=\"string\" foo.c
も同様に
#define HOGE 10
#define HOGE "string"
を加えたのと同じである。foo.c の中でマクロ HOGE の定義が行われていたとしても、-D オプションで指定した値が有効になる。-U オプションでマクロを無効にできる。

自作のソースをコンパイルするときはあまり使わないだろうが、一般に配布されているプログラムは、多くの環境でコンパイルできるようにするため、#ifdef 〜 #else 〜 #endif が山のように埋め込まれている。そのままコンパイルできれば問題ないのだが、もしコンパイル途中にエラーで終了した場合は、あれこれ悩みながら -D オプションでマクロを設定することになる。

また、データの最大数が
#define MAX_DATA 1000
とマクロで定義されているときや、〜という機能を有効にするかどうかをマクロで決定する場合は、それぞれ
% gcc -DMAX_DATA=3000 ....
% gcc -DENABLE_HOGE ....
とすることで、ソースを書き換えることなくコンパイルできる。

ただし、機能のオン・オフや、インストールするディレクトリなどは Makefile を書き換えたり、make の引数でマクロを設定したり、configure のオプションで指定することの方が多い。まずはプログラムに付属するインストールマニュアルを熟読すること。

-U マクロを無効化する
% gcc -UHOGE foo.c
とすると、foo.c の中のマクロ HOGE が無効になり、
#ifdef HOGE
が偽になる。マクロを設定するには -D オプションを使う。

-I インクルードファイルのパスを指定
インクルードファイルを取り込む場合は、ソースの中に
#include <stdio.h>
#include <sys/stat.h>
#include <X11/Xutil.h>
など書く。インクルードファイルが /usr/include/ 以下にあるなら、-I オプションを指定しなくても
/usr/include/stdio.h、/usr/include/sys/stat.h
がインクルードされる。しかし X11/Xutil.h は実際は /usr/X11R6/include/X11/Xutil.h にあるので、
-I/usr/X11R6/include
と、インクルードファイルのパスを指定しなければならない。
-l ライブラリを指定
例: -lm -lnsl -lX11
例えばソース中で sin() を使った場合、sin() のライブラリを指定しないとリンク時にエラーとなる。sin() のライブラリは /usr/lib/libm.a なので、「libm.a」から「lib」と「.a」を取り除いて「-lm」と指定する。同様に、X Window System のライブラリ関数は、/usr/X11R6/lib/libX11.a で定義されているので「-lX11」とする (ただし -L オプションでライブラリパスの指定が必要)。printf、strlen などの基本的な関数は、全て /usr/lib/libc.a というライブラリに 含まれているが、libc.a は標準でリンクされるので -lc と指定する必要はない。

なお、目的のライブラリ関数が、どのライブラリに入っているかは nm コマンドで探すとよい。
% nm -o /usr/lib/*.a /usr/X11R6/lib/*.a | grep sin
-L ライブラリのパスを指定
例: -L /usr/X11R6/lib
上で説明したライブラリのあるディレクトリにパスが通っていなかった場合、そのディレクトリを -L オプションで指定する。例えば X11 関係の関数を使用した場合、/usr/X11R6/lib/libX11.a をリンクする必要があるので -lX11 とする。しかし標準設定では /usr/X11R6/lib がライブラリの検索対象となっていないので、-L /usr/X11R6/lib とする必要がある。

その他
昔は GCC は「GNU C Compiler」の略だったが、 現在は「GNU Compiler Collection」の略になった。C や C++、Objective-C に加え、Fortran・Ada・Go までコンパイルできるらしい。一時期は Java にも対応していた (GCJ) が、開発が停滞していたため 2016年に GCC より削除された。
>> FreeBSDオンラインマニュアル(man) FreeBSD gcc(1)
>> Linuxオンラインマニュアル(man) Linux gcc(1)