6日でできる 新C言語入門|デバッガの基本と使い方

バグは勘で探すより、動きを見ながら確かめよう。
デバッガを使えば、C言語プログラムの流れ、変数、配列、ポインタの状態を目で確認できます。

C言語でプログラムを書いていると、コンパイルは成功しているのに、実行結果が思った通りにならないことがあります。

たとえば、計算結果が違う、配列の値が途中で変わっている、関数に渡した値が想定と違う、ポインタがどこを指しているのか分からない、といった場面です。

このようなときに役立つのがデバッガです。

デバッガは、プログラムを実行しながら、途中で一時停止したり、1行ずつ処理を進めたり、変数や配列の値を確認したりできる開発支援ツールです。プログラムの中で何が起きているのかを、実行中に観察できるため、バグの原因を探すときにとても役立ちます。

特にC言語では、配列、ポインタ、関数、ループ処理など、実行中の状態を確認しながら理解したい場面が多くあります。デバッガを使うと、コードを読むだけでは見えにくい処理の流れを、実際の値の変化として確認できます。

この記事では、Visual Studioを例に、デバッガの基本操作、ブレークポイントの使い方、ステップイン、ステップオーバー、ステップアウトの違い、変数や配列、ポインタの確認方法を、サンプルプログラムを使ってやさしく解説します。

デバッガとは何か

デバッガは、プログラムの実行中の状態を調べるためのツールです。

通常の実行では、プログラムは上から下へ一気に処理されます。処理が終わったあとに実行結果だけを見ても、途中でどの変数がどの値になっていたのか、どのif文に入ったのか、関数の中で何が起きたのかまでは分かりにくいです。

デバッガを使うと、プログラムを途中で止めて、次のようなことを確認できます。

操作説明
ブレークポイント指定した行でプログラムを一時停止する
ステップイン関数の中に入り、1行ずつ処理を追う
ステップオーバー関数呼び出しを1行分の処理として実行する
ステップアウト現在の関数を最後まで実行し、呼び出し元へ戻る
変数ウォッチ変数、配列、ポインタの値を確認する

デバッガは、Visual Studio、Code::Blocks、Eclipseなど、多くの統合開発環境に用意されています。

ここではVisual Studioを例にしていますが、基本的な考え方は他の開発環境でも共通しています。

デバッガを使うと何が分かるのか

デバッガを使うと、プログラムの内部を順番に確認できます。

たとえば、for文の中で変数iがどのように増えていくのか、if文の条件がtrueになるのはどのタイミングなのか、配列の各要素にどの値が入っているのかを確認できます。

ポインタを使うプログラムでは、ポインタ変数が持っているアドレスや、ポインタが指している先の値も確認できます。

学習テーマデバッガで確認できること
変数現在の値、型、変化のタイミング
配列各要素の値、添え字ごとの変化
ポインタ保存しているアドレス、参照先の値
関数引数、戻り値、処理の流れ
ループカウンタ変数、繰り返し回数、条件判定

デバッガは、バグを見つけるためだけの道具ではありません。C言語の仕組みを理解するための学習ツールとしても、とても便利です。

図:デバッガでプログラムの流れを見える化する

この図から分かること

この図から分かるのは、デバッガがプログラムの途中経過を確認するための道具であるということです。

ブレークポイントで処理を止め、ステップ実行で1行ずつ進めることで、変数や配列、ポインタの状態を確認できます。実行結果だけでは分からない途中の動きを見られるため、バグの原因を探すときにも、C言語の理解を深めるときにも役立ちます。

Visual Studioでの基本的な流れ

Visual Studioでデバッガを使う基本的な流れは、次のようになります。

手順内容
停止したい行を決める確認したい処理の行を選ぶ
ブレークポイントを設定する行番号の左側をクリックして赤い丸を付ける
デバッグ実行を開始するローカルWindowsデバッガーなどから実行する
停止した行で状態を確認する変数、配列、ポインタの値を見る
ステップ実行する1行ずつ進めて値の変化を追う
必要に応じて続行または停止する次のブレークポイントへ進むか、終了する

通常の実行では、プログラムは最後まで一気に動きます。デバッグ実行では、ブレークポイントを置いた行で一時停止できます。

一時停止している間に、現在の変数の値を確認したり、次に実行される行を見たりできます。

デバッガでよく使う操作

デバッガにはいくつかの操作があります。最初はすべてを覚える必要はありませんが、ブレークポイント、ステップイン、ステップオーバー、ステップアウトはよく使います。

ボタンのイメージ名前主な用途
続行次のブレークポイントまで実行を進める
停止デバッグ実行を終了する
ステップイン1行だけ処理を進める。関数呼び出しの場合は関数の中に入る
ステップオーバー1行だけ処理を進める。関数呼び出しの場合は関数全体を実行する
ステップアウト現在の関数の残りを実行して呼び出し元へ戻る

操作の流れは、たとえば次のようなイメージです。

[▶] → [↓] → [→] → [↑] → [■]

続行で次の停止位置まで進み、ステップインやステップオーバーで細かく処理を追い、必要がなくなったら停止します。

サンプルプログラムでデバッガを体験する

ここでは、配列の中から最大値を求めるプログラムを使って、デバッガの動きを確認します。

このプログラムでは、main関数で配列を用意し、find_max関数に配列の先頭アドレスと要素数を渡しています。find_max関数では、配列を先頭から順に調べ、最大値を見つけます。

プロジェクト/ファイル名: Lesson58_1/main.c

#include <stdio.h>

// 配列の最大値を求める関数
int find_max(int*, int);

int main(void) {
    int data[5] = {7, 2, 9, 4, 6};
    printf("配列の内容:");
    for (int i = 0; i < 5; i++) {
        printf("%d ", data[i]);
    }
    printf("\n");

    int max_value = find_max(data, 5);
    printf("最大値:%d\n", max_value);

    return 0;
}

int find_max(int* arr, int size) {
    int max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

実行結果

配列の内容:7 2 9 4 6
最大値:9

このプログラムでは、data配列に7、2、9、4、6が入っています。

find_max関数では、最初にmaxへarr[0]の値を入れます。そのあと、for文でarr[1]からarr[4]までを順番に調べ、現在のmaxより大きい値があればmaxを更新します。

最終的に、最大値として9が返されます。

ブレークポイントを設定する

デバッガを使うときは、まず処理を止めたい行にブレークポイントを設定します。

Visual Studioでは、行番号の左側をクリックすると、赤い丸「」が表示されます。この赤い丸「」がブレークポイントです。

今回のプログラムでは、次の行にブレークポイントを置くと、最大値探索が始まる直前の状態を確認できます。

int max = arr[0];

この行で止めると、find_max関数に入った直後の状態を確認できます。

確認したい主な内容は次の通りです。

確認項目見るポイント
arrdata配列の先頭アドレスを受け取っているか
size配列の要素数として5が渡されているか
arr[0]最初の値7が見えるか
maxこれから最大値候補として初期化される

ブレークポイントは、バグが起きそうな場所や、値の変化を確認したい場所に設定します。

デバッグ実行を開始する

ブレークポイントを設定したら、デバッグ実行を開始します。

Visual Studioでは、画面上部にある「ローカルWindowsデバッガー」などの実行ボタンからデバッグを開始できます。

通常の実行と違い、デバッグ実行ではブレークポイントでプログラムが一時停止します。一時停止した状態では、現在どの行で止まっているのか、変数がどの値を持っているのかを確認できます。

このとき、画面には次に実行される行が強調表示されます。プログラムはその行をまだ実行していない状態で止まっていることが多いので、今から何が実行されるのかを意識しながら確認すると分かりやすいです。

ステップインで関数の中に入る

main関数の中には、次の関数呼び出しがあります。

int max_value = find_max(data, 5);

この行でステップインを使うと、find_max関数の中へ入れます。

ステップインは、関数の内部処理を詳しく追いたいときに使います。

たとえば、find_max関数の中へ入ると、次のような処理を1行ずつ確認できます。

int max = arr[0];

for (int i = 1; i < size; i++) {

if (arr[i] > max) {

max = arr[i];

関数内部への追跡(ステップイン)

 たとえば、find_max関数を呼び出している部分で「ステップイン」すると、mainからfind_max関数の処理に移動し、引数や配列のポインタの中身、各ループの処理などを1ステップずつ確認できます。

1.関数呼び出しのことろで、ステップイン「↓」します。

2.find_max関数の処理に移動します。

3.「ステップイン」ボタンをクリックします。

引数や配列のポインタの中身、各ループの処理などを1ステップずつ確認できます。

4.デバックの停止「」ボタンをクリックします。

デバッグ実行を終了します。

このように、ステップインを使うと、関数の引数がどのように渡されたのか、関数内で値がどのように変化するのかを細かく確認できます。

図:ステップインで関数の中へ入り処理を追う

この図から分かること

この図から分かるのは、ステップインを使うと、main関数からfind_max関数の内部へ移動できるということです。

関数呼び出しの結果だけを見るのではなく、関数の中でarr、size、max、iがどのように使われているかを確認できます。関数の動きを細かく理解したいときには、ステップインがとても便利です。

ステップオーバーで関数呼び出しをまとめて進める

ステップオーバーは、1行だけ処理を進める操作です。

ただし、その行に関数呼び出しがある場合、関数の中には入らず、関数全体を実行して次の行へ進みます。

たとえば、次の行でステップオーバーを使うと、find_max関数の中には入らず、関数の結果だけを受け取って次の行へ進みます。

int max_value = find_max(data, 5);

ステップオーバーは、関数の内部を詳しく見る必要がないときに便利です。

操作関数呼び出しがある行での動き
ステップイン関数の中に入る
ステップオーバー関数の中に入らず、まとめて実行する

find_max関数の内部にバグがありそうならステップインを使います。
find_max関数の中は確認済みで、main関数の流れを見たいだけならステップオーバーを使います。

ステップアウトで呼び出し元へ戻る

ステップアウトは、現在の関数の残りをまとめて実行し、呼び出し元へ戻る操作です。

たとえば、find_max関数の中に入って途中まで確認したあと、残りの処理を細かく見る必要がなくなった場合に使います。

ステップアウトを実行すると、find_max関数の残りが実行され、main関数の呼び出し元の位置へ戻ります。

イメージは次の通りです。

main関数
  └─ find_max関数へ入る
        └─ ステップアウトでmain関数へ戻る

ステップアウトは、関数の中に入りすぎたときや、関数の戻り値を確認したいときに便利です。

find_max関数で確認したい変数の変化

デバッガでは、find_max関数の中で変数がどのように変わるかを追うと、処理の理解が深まります。

今回の配列は次の通りです。

data[0] = 7
data[1] = 2
data[2] = 9
data[3] = 4
data[4] = 6

find_max関数では、最初にmaxへarr[0]を入れます。

int max = arr[0];

この時点でmaxは7です。

そのあと、iを1から4まで変化させながら、arr[i]とmaxを比較します。

iarr[i]maxの値判定
1272は7より大きくないため更新しない
297から9へ変化9は7より大きいため更新する
3494は9より大きくないため更新しない
4696は9より大きくないため更新しない

この表の流れを、デバッガで1行ずつ確認できます。

特に、if (arr[i] > max) の行に注目すると、どのタイミングでmaxが更新されるかが分かります。

変数ウィンドウやウォッチで確認できる情報

デバッグ実行中は、変数ウィンドウやウォッチウィンドウを使って、現在の変数の値を確認できます。

たとえば、find_max関数の中では次のような情報を確認できます。

名前現在の値の例備考
data7, 2, 9, 4, 6int配列main関数側の配列
arrアドレス値int*data配列の先頭を指す
size5int配列の要素数
max7、9などint最大値候補として変化する
i1から4intfor文のカウンタ

配列の場合は、dataやarrを展開すると、各要素の値を確認できます。

ポインタの場合は、ポインタ変数が持っているアドレスと、そのポインタが指す先の値を確認できます。

たとえばarrはint*型なので、配列の先頭アドレスを持っています。arr[0]、arr[1]のように表示すれば、配列の各要素も確認できます。

配列とポインタの確認にデバッガが役立つ理由

C言語では、配列を関数に渡すと、関数側では先頭アドレスを受け取ります。

今回のプログラムでも、main関数側のdata配列をfind_max関数に渡しています。

int max_value = find_max(data, 5);

find_max関数側では、int* arrとして受け取ります。

int find_max(int* arr, int size)

つまり、arrはdata配列の先頭を指しています。

デバッガを使うと、このarrがどのアドレスを持っているのか、arr[0]からarr[4]までにどの値が入っているのかを確認できます。

配列とポインタの関係は、コードを読むだけでは少し分かりにくいことがあります。デバッガで実際の値を見ながら確認すると、配列名、ポインタ、先頭アドレスの関係が理解しやすくなります。

図:デバッガで配列とポインタの状態を確認する

この図から分かること

この図から分かるのは、main関数のdata配列がfind_max関数へ渡されるとき、配列全体がコピーされるのではなく、先頭アドレスが渡されるということです。

find_max関数側のarrは、data配列の先頭を指します。デバッガを使うと、arrが指す先、arrの各要素、maxの変化、iの変化を実行中に確認できます。配列やポインタの理解を深めるうえで、デバッガはとても役立ちます。

ブレークポイントを置くおすすめの場所

今回のプログラムでは、次の場所にブレークポイントを置くと、処理の流れを確認しやすくなります。

場所確認できること
int max = arr[0];最大値探索を始める直前の状態
for (int i = 1; i < size; i++)ループが始まるタイミング
if (arr[i] > max)比較条件がどう判定されるか
max = arr[i];最大値が更新される瞬間
return max;関数が返す最終結果

特に、max = arr[i];の行で止めると、最大値が更新される瞬間を確認できます。

今回のデータでは、arr[2]が9なので、iが2のときにmaxが7から9へ変わります。この変化をデバッガで見ると、最大値探索の仕組みがとても分かりやすくなります。

ステップ実行で見る処理の流れ

ステップ実行では、1行ずつ処理を進めながら変数の変化を確認します。

今回のfind_max関数では、次のような順番で確認するとよいです。

確認順見る内容
1arrがdata配列の先頭を指しているか
2sizeが5になっているか
3maxにarr[0]の7が入るか
4iが1から始まるか
5arr[i]とmaxの比較結果を見る
6maxが更新されるタイミングを見る
7最後に9が返されるか

このように、デバッガでは処理の意味を1つずつ確認できます。

プログラムが正しく動いている場合でも、あえてデバッガで追ってみると、C言語の処理の流れがかなり理解しやすくなります。

デバッガで学習するときのポイント

デバッガは、バグ修正だけでなく学習にも役立ちます。

変数が変わる瞬間を見る

変数の値は、プログラムの行が実行されるたびに変化します。

たとえば、maxは最初7ですが、arr[2]の9を見つけたときに9へ変わります。この変化をウォッチウィンドウで見ながらステップ実行すると、最大値探索の考え方が分かりやすくなります。

配列を展開して要素を見る

配列は、各要素が並んだデータです。

デバッガでは、dataを展開してdata[0]からdata[4]までの値を確認できます。配列の添え字と実際の値を見比べることで、ループ処理の理解が深まります。

ポインタが持つアドレスを見る

arrはint*型のポインタです。デバッガでは、arrが持っているアドレスや、arr[0]、arr[1]のように参照した値を確認できます。

ポインタの学習では、アドレスと値の違いが重要です。デバッガを使うと、ポインタがどこを指しているかを実際に確認できます。

関数の引数と戻り値を見る

find_max関数には、dataと5が渡されています。

関数の中では、それぞれarrとsizeとして受け取ります。デバッガで確認すると、呼び出し元の値が関数側でどのように使われているかが分かります。

デバッガで確認したい学習シーン

C言語の学習では、次のような場面でデバッガを使うと理解が深まります。

活用シーン確認できる内容
ポインタの仕組みを学ぶアドレス、参照先の値、ポインタの変化
配列処理を学ぶ添え字、要素の値、ループごとの状態
関数を学ぶ引数、戻り値、呼び出し元との関係
if文を学ぶ条件がtrueになるタイミング
for文を学ぶカウンタ変数の変化、繰り返し回数
バグを探す期待と違う値になった場所

デバッガを使うと、プログラムの動きを想像だけで追う必要がなくなります。実際の値を見ながら確認できるため、理解しやすくなります。

デバッガを使うときの注意点

デバッガを使うときは、次の点も意識しておきましょう。

停止している行はまだ実行前の場合がある

デバッグ中に黄色い矢印などで示されている行は、次に実行される行であることが多いです。

つまり、その行の処理結果はまだ変数に反映されていない場合があります。値を見るときは、今どの行の前で止まっているのかを確認しましょう。

ステップインとステップオーバーを使い分ける

関数の中を詳しく見たいならステップインを使います。
関数の中を見る必要がないならステップオーバーを使います。

毎回ステップインを使うと、標準ライブラリの中に入ってしまう場合もあるため、必要に応じて使い分けるとよいです。

ポインタの値と参照先の値を区別する

ポインタ変数はアドレスを持っています。
そのアドレスが指す先に値があります。

デバッガでポインタを見るときは、ポインタ自身の値と、ポインタが指す先の値を分けて確認しましょう。

ブレークポイントを増やしすぎない

ブレークポイントをたくさん置きすぎると、どこで何を確認したいのか分かりにくくなります。

まずは、処理の開始地点、値が変わる地点、関数の戻り地点など、重要な場所に絞って設定すると見やすくなります。

デバッガを使いこなすとC言語の理解が深まる

デバッガは、C言語プログラムの動きを目で確認するための強力なツールです。

ブレークポイントを使えば、確認したい行でプログラムを一時停止できます。ステップインを使えば関数の中に入り、ステップオーバーを使えば関数の中に入らず処理を進められます。ステップアウトを使えば、現在の関数を抜けて呼び出し元へ戻れます。

今回のLesson58_1では、配列の最大値を求めるプログラムを使って、data配列、arrポインタ、size、max、iの変化を確認できます。特に、maxが7から9へ更新される瞬間を見ると、最大値探索の処理がとても分かりやすくなります。

C言語では、配列やポインタ、関数の引数、ループ処理など、実行中の状態を見ることで理解しやすくなるテーマがたくさんあります。デバッガを積極的に使うことで、バグを見つける力だけでなく、プログラムの仕組みを読み解く力も身につきます。