Cコンパイラ,デバッガ
1 コンパイラ
高級言語でプログラムを書いて,そのプログラムを実行させるには,プログラム を機械語に変換しなければなりません.この変換作業を行うのがコンパイラです. 使用するコンパイラは,言語により異ります.もちろんシステムにも依存します. C,C++言語のコンパイラとしては,FreeBSDでは,gcc, g++というコンパイラが使 用できる.Solarisでは,gccや g++も使用できるが,CCというコンパイラも 使用できる.コンパイラは以下の手順でプログラムをコンパイルします.以下, ある言語で書いたプログラムをソースプログラムと呼びます.- プリプロセッサ
ソースプログラム中のコメントアウトを除去したり,#で始まる行 (#define, #include, #ifdef)などを処理します.
- コンパイラ本体
1.で生成されたファイルからアセン ブリ言語ソースファイルに変換します.アセンブリ言語とはシステムの プロセッサが実行することができる命令でプログラムを作成する言語で す.アセンブリ言語はシステムに依存します.アセンブリ言語ソースファ イルは,テキスト形式であるのでシステムのアセンブリ言語を勉強すれ ば読むことは可能です.
- アセンブラ
2.で生成されたアセンブリ言語ソースファイルを機械語 で表したオブジェクトファイルに変換します.オブジェクトファイルと は,アセンブリ言語ファイルの文字列をすべて0か1の数字の並びに置き 換えてバイナリ形式にしたファイルのことです.
- リンカ
最終的に,プログラムを実行できるようにするためにオブジェクトファ イルから,ロードモジュールを生成します.ロードモジュールとは複数の オブジェクトファイルやライブラリが結合されて実行可能となったもの です.
1.1 gcc, g++ コンパイラの使い方
gccはC言語用,g++はC++言語用コンパイラです.以下 gccを用いてコンパイル方 を説明するが g++ でも同様に使用できる.sample.cというCプログラムソー スファイルをコンパイルするときは以下のようにします.
% gcc sample.c
このようにすると,a.outという実行ファイルが作成される.ここでは,上で
述べた1.から4.までの処理をすべて行っている.gccではオプションを付け
るか,コンパイルエラーがでない限り,リンカまでの処理をすべて行う.
実行ファイル名をa.out以外の名前にしたい場合は以下のように -o オプション
を付けてコンパイルします.
% gcc sample.c -o sample
ソースプログラムをコンパイルして出来た実行ファイルを実行する場合は,以下 のようにします.(ここで実行ファイルの名前は sample とします)
% ./sample
- -E
プリプロセッサ処理のみを行う.% gcc -E sample.c
とすると,ソースファイル sample.cのコードに対してプリプロセッサ処理を 行った結果のコードを標準出力に出力します.
- -S
コンパイル処理までを行う.アセンブル言語ソースファイルを生成.% gcc -S sample.c
とすると,ソースファイル sample.cのコードをアセンブリコードに変換 したファイル sample.sを生成します.
- -c
アセンブル処理までを行う.オブジェクトファイルを生成.% gcc -c sample.c
とすると,ソースファイル sample.cをコンパイルして,オブジェクトファ イル sample.oを生成します.
- -v
標準エラー出力に,コンパイル時に実行したコマンドと Versionを 表示す.
- -Wall
いろいろな場合の警告を表示す.詳細については manを見てくださ い.
- -g
デバッガを使ってデバックを行う場合にこのオプションを使用する.
1.2 コンパイルエラーの対処法
コンパイルを行うと必ずと言っていいほどエラーがでます.エラーに対処するに
は,エラーがでている場所と,どうしてエラーがでているかをつきとめなければな
りません.コンパイラはエラーがでている場所を教えてくれて,それがどういう
エラーかも教えてくれます.ただしコンパイラが正確にエラーの場所と種類を教
えてくれるとは限りません,エラーの場所とその内容を探すには,経験が重要で
す.以前に見たことがあるエラーならば,それの対処法はだいたいわかります.
これからコンパイルエラーの例を挙げて,コンパイルエラーの意味と対処法を説
明します.
以下のようなC言語のソースファイルを sample.c とします.(左端の数字は行番
号を表していて実際のファイルには存在しません.)
1. int main(){ 2. int i = 3; 3. int j = 5; 4. k = i + j; 5. } |
このソースをコンパイルすると以下のようなエラーがでます.
mine@eric[src]% gcc sample.c -o sample sample.c: In function `main': sample.c:4: `k' undeclared (first use in this function) sample.c:4: (Each undeclared identifier is reported only once sample.c:4: for each function it appears in.) |
- このエラーの1行目は,sample.c ファイルの main関数の中でエラーがで たことを示している.
- このエラーの2行目は,sample.c の4行目でエラーがでていて,そのエラー の内容は,変数 kが宣言されていないということである.
- 3行目と4行目はエラーの補足である.(宣言されていない変数のエラーの 表示はその関数の中で初めて現れた1度目しか報告しません)
このように,コンパイラはエラーコードのファイル名と場所,その内容を教えて
くれるのでそれを見て,ソースファイルを修正すればよい.この場合は,変数 k
を宣言すれば,コンパイルエラーは解決される.
2 デバック
コンパイルができてプログラムが実行できも,最初から快調に動くことはまずあ
りません.大規模なプログラムになればなるほどこの事はよく当てはまります.
つまり,コードの中にバグがあるのです.デバックとはこのバグを
見つけ出し,バグを直すことを言います.
2.1 バグの再現
バグが起ったらそのバグは,どういった場合に発生するのかを詳しく突き止める
必要がある.たとえば,ある入力を与えるプログラムならば,どのような入
力のときにバグが起るかを突き止めます.そうすることにより,バグを再現しな
がらデバックすることが簡単になる.
また,バグを見つけると,そこの
コードを変更するわけですが,自分がここが怪しいと思ってコードを書き直して
も,バグが無くなるとは限りませんし,もしかしたらもっとひどいバグになるか
も知れません.またコードを書き直したら,違うバグがでてきたということもあ
ります.ですから,バグがあってそのコードを書き直すときは,ソースのバッ
クアップを取ることによりバグを再現できるようにしてください.そうすること
で,違ったバグがでてきたとしてもソースを見比べたり,実行結果を比較するこ
とによりバグの箇所がわかることがあります.また,ソースのバックアップを取っ
ておくことは,大規模なプログラムをデバックしていてどこをどう変更したかわ
からなくなったときなどにも役に立ちます.
2.2 自己検証コード
バグの場所を見つけるのに,一番簡単な方法はいろいろな情報を出力させること
です.例えば,プログラムが途中で止まってしまうときなどには,どこまでプ
ログラムが動いているかを知ることが重要です.そこで,プログラム中にメッセー
ジを表示させるコードをいくつか書いて,実行中にどのメッセージが表示された
かを見ることにより,プログラムの停止箇所がわかります.また,値を計算する
するプログラムの解がおかしな値になっていたとしたら,計算途中の値などを表
示させることも有力な手がかりとなる.多くのバグは,簡単なプログラミン
グのミスから生じますので,バグの場所がわかれば,ほとんどの場合バグは簡単
に解決します.
2.3 デバッガ
デバッガとは,デバックの手助けをしてくれるツール.デバッガを使うと,
プログラムを1行づつ実行したり,特定の行でプログラムを停止することができ
ます.また実行を停止した時点での変数の値や関数に評価されたあとの値などの情
報も見ることができる.デバッガを使ってプログラムを実行しているときにプロ
グラムが異常終了すると,デバッガはその位置を覚えていてくれて,どこでプログ
ラムが停止したかを即座に知ることができる.デバッガはこのような高度な機
能を持っているので,デバッガを使うことによりデバック作業の効率は大幅に増
加します.
デバッガもコンパイラと同様で,言語やマシンに依存したものです.またデバッ
カはコンパイラにも依存します.gccでコンパイルしたものは gdbというデバッ
ガが使える.Solarisの CCコンパイラでコンパイルしたものだったら dbxとい
うデバッガが使用できる.以下,gdbの使い方について説明する.
2.4 gdb(デバッガ)の使い方
gdbは以下のようにして使う.
% gdb sample
gdbを起動します.起動後は プロンプト に(gdb)と表示される.ここでsampleは,実行ファイ ルです.またこの実行ファイルはコンパイル時に -g オプショ ンを付けてコンパイルされたものでなければいけません.
(gdb) quit
gdbを終了します.
gdbコマンドには,以下のようなものがあります.
(gdb) info file
- 現在読み込んでいるファイルの情報を表示す.
(gdb) file filename
- ファイル名が filenameである実行ファイルを読み込みそ
の実行ファイルについて,デバック可能になる.
(gdb) run
- 現在読み込んでいる実行ファイルの実行を開始します.
(gdb) kill
- 現在デバックしているプログラムを停止させて終了させます.
(gdb) break [filename:]num
- ブレークポイントを
filenameの num行目に作る.ブレークポイントとはそ
こでいったんプログラムの実行を停止するポイントです.
filenameが指定されていない場合は,ファイルは現在実行
しているコードがあるファイルとなる.
(gdb) clear [filename:]num
- filenameの num行目にあるブレークポイントを削除します.
(gdb) info breakpoints
(gdb) step [num]
- プログラムを1ステップだけ実行する.numが指定してあっ
たらnumステップだけ実行する.
(gdb) next [num]
- プログラムをソースコードの1行だけ
実行する.numが指定してあったらnum行だけ実行する.
この場合上の stepと違って,サブルーチンには入りませ
ん.もし次に実行するコードがサブルーチンだとそのサブ
ルーチンをすべて実行して,戻ってきた時点で終了します.
(gdb) continue
- プログラムが停止した場所から実行を再開します.途中で
ブレークポイントがあるか,プログラムを終了するまで継
続して実行する.
(gdb) finish
- 現在実行している,関数を抜けるまでプ
ログラムを実行する.
(gdb) where
- 現在どこを実行しているかを表示す.どの関数から呼
ばれてどの関数にいるかを表示す.
(gdb) print exp
- exp を評価してそれを表示す.expは変数でもいいし,
式でもかまいません.
(gdb) list
- 現在実行しているところのソースファイルを表示す.
(gdb) help comand
- command のヘルプを表示す.commandを省略すると,
ヘルプの一覧を表示す.
この他にもたくさんのコマンドがありますので,helpで調べてください. それでは,gdb の使い方の例を挙げてみます. 以下のような sample.cと output.cというプログラムがあるとします.
sample.c
1. void output(int value) 2. 3. int 4. main(){ 5. int value = 100; 6. int i; 7. for(i = 0 ; i < 5 ; i++) 8. output(value); 9. } |
1. #include<stdio.h> 2. 3. void 4. output(int value){ 5. printf("%d\n",value); 6. } |
- まずこれらのファイルをコンパイルします.この時にコンパイルオプショ
ン -gを付けます.このオプションを付けないと gdbでデバック作業は行
えません.以下のようにコンパイルします.
% gcc -g -c sample.c output.c
- オブジェクトファイル sample.o output.oが生成される.
% gcc -g sample.o output.o -o sample
- 実行ファイル sampleが生成される.
- 出来た実行ファイルを普通に実行すると以下のようになる.
mine@eric[src]% ./sample 100 100 100 100 100
- それでは,今度は gdbを使って sampleを実行してみる.以下のように
して実行する.メッセージが表示されて,(gdb)となれは gdb
モードになっています.
mine@eric[src]% gdb sample GNU gdb 4.18 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-unknown-freebsd"... (gdb)
- まずブレークポイントを指定します.sample.cの8行目にブレークポ
イントを指定します.
(gdb) break 8 Breakpoint 1 at 0x8048504: file sample.c, line 8.
- プログラムを実行する.
(gdb) run Starting program:/.amd_mnt/shiozaki/home/4f/mine/study/free/CC/src/sample Breakpoint 1, main () at sample.c:8 8 output(value);
ブレークポイントである sample.cの8行目で停止しいています.
- 変数 iと valueの値を表示す.
(gdb) print i $1 = 0 (gdb) print value $2 = 100
- ステップ実行する.
(gdb) step output (value=100) at output.c:5 5 printf("%i\n", value);
output.cの5行目,関数 outputの中で停止している.
- どうのようにして関数outputに来ているか表示す.
(gdb) where #0 output (value=100) at output.c:5 #1 0x8048510 in main () at sample.c:8 #2 0x8048459 in _start ()
これは下から読みます.つまりスタックを表示している._start()はシ ステム側のものです.#1からがユーザプログラムです.現在 sample.c 8行目,main関数から,output.cの関数 outputが呼び出され,output.c の5行目で停止していることを表している.
- continue コマンドを実行する.
(gdb) continue Continuing. 100 Breakpoint 1, main () at sample.c:8 8 output(value);
2行目でvalue(=100)を出力している.ブレークポイントである sample.cの8行目で停止している.
- nextコマンドを実行する.
(gdb) next 100 7 for( i = 0; i < 5; i++ )
前の stepコマンドのとの違いをみてください.output関数の中を処理し たあとに,sample.cの次の行で停止している.value(=100)が出力され ているいます.
- print コマンドで関数の値を評価してみる.
(gdb) print output(10) 10 $3 = void
関数 outputの引数として10を与えた場合の式を評価している.1行目で 10が出力されている.2行目の値は関数 outputの返り値です.
- ブレークポイントを消去して,最後までプログラムを実行する.
(gdb) clear 8 Deleted breakpoint 1 (gdb) continue Continuing. 100 100 100 Program exited with code 04.
ブレークポイントを削除して,continueコマンドを使用すると,プログ ラムの最後まで実行している.value(=100)を3回出力してプログラム を終了している.