さきほどのhello.cをコンパイルリンクして実行してみましょう。
% cc hello.c
ccはCコンパイラ呼出しですが、この命令はほっておくと実行可能なファイル まで作ってしまいます。ccがリンカーまで呼び出してしまうのです。リンカー はldといいますが、ldを単独で呼び出すと、C言語の使うライブラリーを明示 して呼び出す必要があるため、面倒です。ccはCで必要なライブラリーを取り 出してリンクするようにldに命令します。ですから、コンパイルせずリンクす るためだけにもccを使います。ccは狭い意味のコンパイラではありません。C の言語プロセッサと考えるとよいでしょう。これは他の言語についても言えま す。f77やcxxもしくはCC(C++のこと)も同じように使います。
さて、では出来上がった実行可能ファイルを呼び出してみましょう。
% a.out Hello! World. %
これもUNIXらしい所ですが、ccは何もいわないでおくと実行可能ファイル a.outを作ります。lsで確かめてください。ldの作った実行可能形式ファイル をa.outファイルということもあります。当然ですがそれでは困ります。今の 場合実行可能ファイルにはhelloという名がつくのが自然ですね。そのように 指定することは当然できます。
% cc -o hello hello.c
-o helloというスイッチが実行可能ファイルの名前をhelloにしなさいと指定 しています。めでたく、
% hello Hello! World. %
つぎに下のようなプログラムを書いてみましょう。ファイル名をargs.cとしま す。
/* args.c */ #include <stdio.h> main( argc, argv ) int argc; char **argv; { int i; for( i = 0; i < argc; i++ ) printf( "arg #%d = %s\n", i, *argv++ ); }
これをコンパイルします。
% cc -o args args.c
Cプログラムではそれが呼び出されるときに引数リストがargc、argvという二 つのmainへの引数で渡されます。argcには引数の数が、argvは引数リストへの ポインタが入ります。第1番目の引数はコマンドの名前です。このargsという プログラムは引数の値を順に表示するプログラムです。実行してみましょう。
% args arg #0 = args % args abc arg #0 = args arg #1 = abc % args "This argument contains spaces" arg #0 = args arg #1 = This argument contains spaces % args * arg #0 = args arg #1 = args arg #2 = args.c arg #3 = hello arg #4 = hello.c %
プログラムに引数がどのように渡されるかがよくわかります。最後の例では* をシェルがそのディレクトリ内のファイル名に置き換えました。詳しいことは Cの教科書を見てください。
ここまではファイル1つだけで作る簡単なプログラムでした。実際にはいくつ かのファイルに分割されたプログラムを書くことの方が多いでしょう。その場 合は他のシステムと同じように個々のファイルをオブジェクトモジュールにコ ンパイルして、最後にそれらをリンクして実行可能イメージを作ります。例え ばmain.cとsub.cからtestというプログラムを作ることを考えましょう。 main.cからmain.oを、sub.cからsub.oをつくり、最後にmain .oとsub.oから testを作ります。
% cc -c main.c % cc -c sub.c % cc -o test main.o sub.o
最初の2行ではmain.cとsub.cをそれぞれコンパイルしています。-cというス イッチはコンパイルのみを行ない、ソースファイルの名前から.cをとり.oをつ けてオブジェクトモジュールとして保存させるものです。最後の行はソースが みんなオブジェクトモジュールなのでリンクだけしてくれます。-o testは実 行可能ファイル名をtestにします。ccの詳しい説明はman ccで見てください。
\subsection{ライブラリーをつくる} 複数のファイルを作ってプログラムを書くようになると、たくさんのオブジェ クトモジュールができてきて、それらの名前を一つ一つccコマンドの中で書く ことが面倒になります。こういうときはオブジェクトコードのライブラリーを 作ってcc(正確にはld)にそこから必要なものを探してもらうようにします。ラ イブラリーを作ってみましょう。関数が記述されたファイルをsub1.c、sub2.c、 sub3.cとします。それぞれをコンパイルします。 \begin{verbatim} % cc -c main.c % cc -c sub1.c % cc -c sub2.c % cc -c sub3.c % ls sub1.c sub2.c sub3.c main.c sub1.o sub2.o sub3.o main.o %
実行可能ファイルを作るのに
% cc -o test main.o sub1.o sub2.o sub3.o
とやってもよいのですが、サブルーチンの集合をライブラリーにします。
% ar r mylib.a sub1.o sub2.o sub3.o ar: creating mylib.a % ranlib mylib.a % cc -o test main.o mylib.a
まずarはアーカイバーです。複数のファイルを集めて一つのアーカイブファイ ルにします。第1引数rは置き換えを意味します。mylib.aの中に同じ名前のファ イルがあれば置き換えます。なければ追加します。アーカイブファイルの中に 何が入っているかは、
% ar t mylib.a
で見ることができます。詳しくはman arで見てください。次のranlibはアーカ イブファイルをランダムライブラリーに変換します。ldが見てわかる形になり ます。最後にccでtestという実行可能形式を作っています。以後関数ファイル を修正したときは
% cc -c sub2.c % ar r mylib.a sub2.o % ranlib mylib.a % cc -o test main.o mylib.a
などを繰り返せばよいわけです。
ここでライブラリーについて一般的なことを少し説明します。UNIXの標準ライ ブラリーは名前がlibではじまり.aなどで終わります。そして/lib、/usr/lib、 /usr/local/libの中にあります。試しにls /libなどやってみてください。で すから必要なライブラリーをリンクする場合、ccでは-lオプションを使います が、はじめのlibと.から後は省略して与えます。たとえばlibX11.sa.1.0を使 いたいときは-lX11という指定の仕方をします。またそのライブラリーファイ ルが上述したディレクトリの中にないときは-Lオプションで指定します。
% cc -o testX testX.o -L/usr/openwin/lib -lX11
という風になります。共通に使うライブラリーは同じコンベンションで命名す るべきでしょう。
ライブラリーを作る場合、その関数の型や必要な定数などを宣言するファイル を別に用意することになります。通常はそのファイルを#includeでよみこみま す。C言語のシステムではインクルードファイルは通常.hで終わる名前になっ ています。標準的なインクルードファイルは/usr/includeにあります。ccはそ こを探しに行きますが、自分の.hファイルが別のところにあるときは-Iオプショ ンでその場所を指定します。
% cc -c -I~/include sub1.c
この場合は自分のホームディレクトリの下にあるincludeディレクトリから.h ファイルを探させるものです。
arを使ってライブラリを作りましたが、arは任意のファイルをアーカイブする ツールです。言い換えると中身が何かをarは知りません。ですから、いくつも の関数を一つのファイルで記述して.oファイルを作ると、.aにつっこんでも取 り出すときは.oファイルごと取り出されてしまいます。効率的なライブラリを 作るには関数毎別々のファイルにすべきです。でも普通はそんなややこしいこ とはしませんね。一つのファイルに関連するいくつもの関数を定義するほうが ずっと書きやすいはずです。そうやって書かれたファイルを関数単位でばらば らにしてくれるツールがあります。言語に依存するのは当然ですが、f77の場 合はfsplit、Cの場合はcsplitというコマンドが用意されています。あるオブ ジェクトファイルの中にどのようなシンボルが定義されているか調べるコマン ドはnmです。検査の対象はオブジェクトファイルでも実行可能ファイル(a.out 形式)でもライブラリでも結構です。