🐧 Linux 総合学習プラットフォーム
C・ビルドツール ・ 中級

警告とデバッグ情報(-Wall -g)

コンパイルが通っても、潜在的なバグが残っていることはよくあります。-Wall を付けると未使用変数や型の取り違えなど、多くの警告をコンパイラが教えてくれます。さらに厳しく見たいときは -Wextra も加えます。一方 -g はデバッグ情報を実行ファイルに埋め込むオプションで、これを付けてビルドしておくと後で gdb を使ったとき、行番号や変数名つきで追跡できます。開発中は gcc -Wall -g prog.c -o prog のように両方を付けるのが定番です。

コンパイルがエラーなく通ると、つい「これで正しいプログラムができた」と安心しがちです。しかし、コンパイルが通ることと、プログラムが正しく動くことは別の話です。文法的には問題なくても、論理的なミスや危うい書き方が残っていることはよくあります。たとえば値を入れたつもりの変数を実は使っていない、型を取り違えている、初期化し忘れた変数を読んでいる——こうした潜在的なバグは、放っておくと実行時に予期せぬ動作を引き起こします。gcc はこうした「文法エラーではないが怪しい箇所」を警告(warning)として教えてくれる能力を持っています。ただし、その多くは既定では表示されません。警告を積極的に出させ、デバッグの準備を整えるためのオプションが、開発では欠かせません。

警告を出させる -Wall

潜在的な問題を洗い出す最初の一手が -Wall です。これを付けると、未使用の変数、初期化前の変数の使用、型の取り違え、戻り値の暗黙の扱いなど、よくある危険のパターンに対して幅広く警告が出るようになります。名前の Wall は warning all のように見えますが、実際には「すべての警告」ではなく「有効にすべき重要な警告のひとまとまり」を指します。それでも既定よりはるかに多くの問題に気づけるため、開発中はまず -Wall を付けるのが常識とされています。たとえば gcc -Wall prog.c -o prog と打つと、コンパイルは通っても warning: 〜 という指摘が並ぶことがあり、それが将来のバグの芽を事前に摘む手がかりになります。具体例として、if (x = 5) のように、比較のつもりが代入になっている書き間違いは、文法上は正しいためエラーにはなりませんが、-Wall を付けると「代入が条件として使われている」と警告してくれます。こうした「動いてしまうが意図と違う」コードを早期に見つけられるのが大きな価値です。

さらに厳しく見る -Wextra

-Wall でも拾いきれない、より細かい怪しさまで見たいときは -Wextra を加えます。-Wextra は -Wall に含まれない追加の警告を有効にするオプションで、たとえば比較の符号の食い違いや、使われない関数引数など、より踏み込んだ指摘をしてくれます。両方を組み合わせて gcc -Wall -Wextra prog.c -o prog とすると、かなり厳しいチェックがかかります。プログラムの品質を高めたいときや、人に渡すコードを書くときは、この2つをセットで付けるとよいでしょう。さらに厳格にしたい現場では、警告をエラー扱いにして必ず直させる -Werror を併用することもありますが、まずは -Wall を基本に、必要に応じて -Wextra を足す、という段階で十分です。

デバッグ情報を埋め込む -g

警告とは別の目的を持つのが -g です。これはデバッグ情報を実行ファイルに埋め込むオプションです。通常のコンパイルでは、できあがった機械語と、元のソースコードの行番号や変数名との対応関係は失われてしまいます。実行ファイルにとって変数の名前は本来不要なので、最終的な機械語には残らないのです。そのままデバッガで追っても、機械語のアドレスしか分からず、人間には読み解きづらい状態です。-g を付けてビルドしておくと、この対応関係(どの機械語がソースの何行目か、変数の名前は何か、構造体の形はどうか)がデバッグ情報として実行ファイル内に保存されます。その結果、後で gdb のようなデバッガを使ったとき、プログラムの実行を一時停止させる地点であるブレークポイントを行番号や関数名で設定でき、「ソースの何行目で止まり、変数 x の値はいくつか」といった形で、ソースコードに即した分かりやすい追跡ができるようになります。-g を付けても実行速度そのものは変わらず、ファイルサイズがデバッグ情報のぶん大きくなるだけなので、開発中は気軽に付けてかまいません。

開発時の定番の組み合わせ

以上を踏まえると、開発中のコンパイルでは -Wall と -g を両方付けるのが定番になります。具体的には gcc -Wall -g prog.c -o prog のように書きます。-Wall で書いている最中の怪しい箇所をその場で潰し、-g でいつデバッガを使うことになっても困らないようにしておく、という二段構えです。両者は目的が違うので競合せず、同時に付けて問題ありません。慣れてきたら -Wextra を加えた gcc -Wall -Wextra -g prog.c -o prog をひな型にしてもよいでしょう。最適化オプション(-O2 など)と組み合わせると、最適化の影響で変数が消えたり行の対応がずれて見えたりすることがあるため、純粋にデバッグしたい段階では最適化を外しておくほうが追いやすくなります。

よくある失敗と実務での意義

ありがちな失敗は、警告を「エラーではないから」と無視してビルドを続けてしまうことです。warning が出てもコンパイル自体は通り実行ファイルもできてしまうため、つい後回しにしがちですが、warning は将来のバグ予告であることが多く、放置すると後で原因不明の不具合として跳ね返ってきます。とくに大量の警告が出る状態に慣れてしまうと、本当に危険な1件が他に埋もれて見えなくなる、という二次被害も起きます。だからこそ、出た警告は基本的にその場で1つずつ解消し、警告ゼロの状態を保つ、という姿勢が結局は近道です。もう1つの失敗は、-g を付けずにビルドした実行ファイルをいざデバッグしようとして、行番号も変数名も見えずに困るパターンです。後からデバッグする可能性が少しでもあるなら、最初から -g を付けておくのが賢明です。完成品をユーザーに配るときは、サイズを小さくしデバッグ情報を含めない別ビルドにする、という使い分けもありますが、開発のあいだは「-Wall で品質を保ち、-g で調査に備える」を基本動作にしておけば、コンパイラを単なる翻訳機ではなく頼れる相棒として使いこなせます。

この項目に出てくる用語

コンパイルこんぱいる
人が書いたソースコードを、CPUが実行できる機械語へ翻訳する処理。
ブレークポイントぶれーくぽいんと
デバッガで、プログラムの実行を一時停止させる地点。
実行ファイルじっこうふぁいる
そのまま起動して動かせる、完成した機械語のプログラム。

関連コマンド

gccgdb

▶ 学習アプリでこの続きを学ぶ・演習する