広告スペース

第15回 デバッグの技法

C言語のデバッグ方法。printfデバッグとよくあるバグの見つけ方を実践的に解説。

デバッグとは ― バグを見つけて直す技術

プログラムが期待通りに動かないとき、原因を探して修正する作業がデバッグです。C言語はコンパイル型のため、バグは大きく2種類に分かれます。
コンパイルエラー
文法ミスなどでコンパイルが通らない。コンパイラのメッセージが手がかり。
実行時バグ
コンパイルは通るが結果が違う・クラッシュする。printfデバッグで追跡。

デバッグの基本ステップ

🔍
1. 再現する
いつ起きるか確認
📍
2. 場所を特定
printfで範囲を絞る
🤔
3. 原因を推理
なぜ間違うか考える
🛠️
4. 修正&確認
直して再テスト
重要: 「たぶんここだろう」で直すのではなく、必ず原因を確認してから修正しましょう。推測で直すと別のバグを生みます。

コンパイルエラーの読み方

gccのエラーメッセージは ファイル名:行番号:列番号: error: 内容 の形式で出力されます。最初のエラーから順に直すのがコツです。

よくあるコンパイルエラー

error: expected ';' before '}' token
原因: セミコロン ; の付け忘れ。エラーの行の1行前を確認しましょう。
int x = 10   // ← ; が無い!
printf("%d", x);
error: 'pritnf' was not declared in this scope
原因: 関数名のスペルミス(pritnf → printf)、または #include の忘れ。
error: 'x' undeclared (first use in this function)
原因: 変数の宣言忘れ、またはスコープ外で使おうとしている。
warning: format '%d' expects argument of type 'int'
原因: printfの書式と引数の型が不一致。%fにdoubleを渡すべきところで%dを使った等。
コツ: エラーが大量に出ても焦らない!最初の1つを直すと、連鎖的に消えることが多いです。

printfデバッグ ― 最も手軽な調査法

変数の値や「ここまで来た」という目印を printf で出力し、プログラムの挙動を追跡する方法です。初心者でもすぐに使えます。

テクニック集

// テク1: 変数の値を確認
printf("DEBUG: x=%d, y=%d\n", x, y);

// テク2: ここまで来たか確認(通過チェック)
printf("DEBUG: ループ前に到達\n");

// テク3: ループ内の変数をステップごとに表示
for (int i = 0; i < n; i++) {
  printf("DEBUG: i=%d, sum=%d\n", i, sum);
  sum += a[i];
}

// テク4: 条件分岐のどちらに入ったか
if (x > 0) {
  printf("DEBUG: xは正\n");
} else {
  printf("DEBUG: xは0以下\n");
}

実際にやってみよう

以下のコードには「合計を求める」つもりが間違った結果になるバグがあります。printfデバッグで原因を突き止めましょう。
ヒント: DEBUGの出力をよく見てください。i は何から始まっていますか? a[5] は存在しますか?

よくあるバグパターン集

初心者が陥りやすいバグのパターンを知っておくと、デバッグが格段に速くなります。
🐛 Off-by-One エラー(境界値ミス)
ループの開始・終了が1つずれる。i=0 から始めるべきところを i=1 にした、i<ni<=n にした等。
// NG: a[5]にアクセスしてしまう(配列外)
for (int i = 0; i <= 5; i++) sum += a[i];
// OK:
for (int i = 0; i < 5; i++) sum += a[i];
🐛 未初期化変数
変数を宣言しただけで初期化せずに使うと、不定値(ゴミ)が入っている。
int sum;       // ← 初期値はゴミ!
for (...) sum += a[i]; // ゴミ + a[i] = ?
// 正しくは: int sum = 0;
🐛 代入 = と比較 == の混同
if (x = 5) は代入してしまう(常にtrue)。比較は == を使う。
if (x = 5)  // NG: xに5を代入(常にtrue)
if (x == 5) // OK: xが5と等しいか比較
🐛 無限ループ
ループ変数の更新忘れ、または条件が永遠にtrueになる。
int i = 0;
while (i < 10) {
  printf("%d\n", i);
  // i++ を忘れた!→ 永遠に i=0
}
🐛 scanf の & 忘れ
scanf("%d", x) ではなく scanf("%d", &x)。アドレスを渡さないとクラッシュ。
scanf("%d", x);   // NG: クラッシュ!
scanf("%d", &x);  // OK

バグ修正チャレンジ

以下のコードにはバグが3つあります。コードを修正して正しい出力「平均 = 5.00」になるようにしてください。
広告スペース

関連する講座

リファレンス編
第31回 コンパイルエラー辞典
C言語のコンパイルエラー一覧と対処法。gccのエラーメッセージの読み方を完全解説。
リファレンス編
第32回 実行時エラーとSegfault
C言語のSegmentation Faultの原因と対処法。実行時エラーを完全解説。
繰り返し・配列・文字列
第14回 繰り返し(for/while)
C言語のfor文とwhile文の使い方。ループ処理を図解で解説。
← 前の講座
第14回 繰り返し(for/while)
次の講座 →
第16回 初等関数(math.h)

確認クイズ

この講座の理解度をチェックしましょう!

Q1. printf デバッグの目的は?

画面を装飾する
変数の値を途中で確認する
プログラムを高速化する

printf で変数の値や実行経路を出力することで、バグの原因箇所を特定できます。最も基本的なデバッグ手法です。

Q2. デバッガ(gdb など)でできることは?

コードの自動修正
1行ずつ実行して変数の値を確認
コンパイルの高速化

デバッガではブレークポイントの設定、ステップ実行、変数の監視など、詳細なプログラム解析が可能です。

Q3. バグを防ぐための良い習慣は?

一度に大量のコードを書く
こまめにコンパイル・テストする
コメントを一切書かない

少しずつ書いてこまめにテストすることで、バグの発生箇所を素早く特定できます。コメントも重要です。

この記事をシェア
X(Twitter)でシェア Facebookでシェア LINEで送る はてブ

この講座の理解を深めるおすすめ書籍

サイトで動きを理解し、書籍で演習量を補うと効果的です

📘
苦しんで覚えるC言語
MMGames 著
初心者向けの定番入門書。丁寧な解説で基礎を固められます。
Amazonで見る
📗
新・明解C言語 入門編
柴田望洋 著
図解が豊富で、演習問題も充実。大学の教科書としても採用多数。
Amazonで見る
📙
プログラミング言語C 第2版
B.W.カーニハン, D.M.リッチー 著
通称K&R。C言語の原典。基礎を終えた後のステップアップに最適。
Amazonで見る

※ 上記リンクはアフィリエイトリンクです。購入によりサイト運営を支援いただけます。