デバッグ機能を活用しよう

このページはC言語はあまり関係ありません。
Visual Studioの機能の解説です。

デバッグとは

プログラミングをしていると、期待通りの動作をしないことがあります。
ソースコードを見て間違いに気づければ良いのですが、いくらコードを見てもどこが間違っているのかがわからない、ということが多々あります。
特にコードが長く複雑化するほど間違いの発見は困難になります。

そのような場合に、実際にプログラムを動かしながらコードの間違いを見つける方法がデバッグです。

Visual Studioのデバッグ機能

Visual Studioにはデバッグのための便利な機能が搭載されていますので、使ってみましょう。
まずは以下のサンプルコード。

#include <stdio.h>

int main()
{
    double kekka;
    kekka = 2.5 + 3;
    kekka += 10 / 4;
    printf("計算結果: %f", kekka);
    getchar();
}

「5.5」と「2.5」を足し、結果が「8」になるつもりでこのようなコードを書いたとします。
しかし実際の実行結果は「7.5」となってしまいます。

計算結果: 7.500000

コードを見て間違いに気付けない場合は、デバッグ機能を使って間違いを探します。

ブレークポイント

Visual Studioのコードエディタ上で、計算が間違っていそうな行の左側をクリックします。
行数が書かれた列のさらに左側の、背景が灰色になっている所です。
すると、画像のように赤丸がつけられます。
Visual Studioのデバッグ ブレークポイントの設置

この状態でビルドして実行すると、コンソール画面には何も出力されません。
Visual Studio本体の方を見てみると、先ほどの赤丸の箇所に黄色い矢印が付いています。
Visual Studioのデバッグ プログラムがブレークポイントで停止しているところ

これは、この行のコードを実行する直前でプログラムが停止していることを意味します。
コードを停止させるために付けた赤丸をブレークポイントと言います。
コードの実行中、ブレークポイントに到達すると、プログラムはその行を実行する直前で停止します。
(「kekka = 2.5 + 3;」はまだ実行されていません)

ここから一行ずつコードを実行していき、計算結果を確認しながらコードの間違いを見つけます。

ローカルウィンドウ

一行ごとの計算結果を確認するためにはローカルウィンドウを使用します。
Visual Studioのデバッグ ローカルウィンドウ

デバッグ機能を使用するとVisual Studioの画面レイアウトが自動的に変更され、ローカルウィンドウも画面下部に表示されるようになります。
もし表示されない場合は、「デバッグ」メニューから「ウィンドウ」→「ローカル」を選択して表示して下さい。

ローカルウィンドウ内には、現在使用している変数の名前と値、そして型名が表示されています。
上の画像では変数kekkaの「値」が訳の分からない数値になっていますが、これは5行目の実行時点では変数宣言だけで値を代入していないからです。
変数は宣言しただけで初期化していない状態では中身は不定で、初期化する前にその変数を使用することはできません。
(代入はできます)

自動変数ウィンドウ

隣の自動変数ウィンドウには、現在と直前の行で使用された変数や関数の戻り値が表示されています。
「ローカル」に比べて表示されるデータが少ないですが、今現在の処理でどのデータに影響があったかを確認できます。

ウォッチウィンドウ

その隣のウォッチウィンドウは、どの変数の値を表示するかを自分で決めることができます。
Visual Studioのデバッグ ウォッチウィンドウ

ウォッチウィンドウでは単に変数を入力するほか、変数同士の計算なども行うことができます。
Visual Studioのデバッグ ウォッチウィンドウ内での演算

ステップ実行

6行目実行の手前で停止しているプログラムを、一行だけ進めるにはステップインステップオーバーを使用します。
「デバッグ」メニュー内に存在するほか、アイコンでも表示されています。
「F10」キーでステップオーバー、「F11」キーでステップインが実行できます。
Visual Studioのデバッグ ステップ実行
Visual Studioのデバッグ ステップインとステップオーバーのアイコン

ステップとは、一行ずつコードを実行する機能です。
ここではステップオーバーを使用してみましょう。
(ステップインとステップオーバーの違いは後述)

ステップオーバーを一回だけ実行すると、以下のようになります。
Visual Studioのデバッグ ステップオーバーの実行直後

黄色い矢印が一行下の7行目に移ります。
つまり6行目のコードが実行されたことを意味します。

そして、ローカルウィンドウ内の「kekka」の値が変わり、赤字で表示されます。
6行目のコードの実行により、変数kekkaに「5.5」が代入されたことを意味します。
ローカルウィンドウ内では、コードの実行によって値に変更があった変数は赤字で表示されます。

6行目の実行時点では期待通りの値が変数kekkaに代入されていることがわかります。
では、もう一度ステップオーバーを実行してみましょう。
Visual Studioのデバッグ ステップオーバーを二回実行した直後

7行目の実行で変数kekkaに期待通りの値が代入されていないことがわかります。
「10 / 4」はint型同士の計算なので、その計算結果もint型になるため、小数点以下が切り捨てられていることがバグの原因です。

もしこれでもバグの原因に気づけない場合は、変数の中身をより詳細に見られるようにコードを書き直してからデバッグをやり直します。

#include <stdio.h>

int main()
{
    double kekka, kekka2;
    kekka = 2.5 + 3;
    kekka2 =  10 / 4;
    kekka += kekka2;
    printf("計算結果: %f", kekka);
    getchar();
}

変数を一つ増やし、個別に値を確認できるようにしました。
変数kekka2に期待通りの値が代入されていないのがわかります。

デバッグの終了

バグの原因が分かったら、Visual Studio上部の「続行」ボタンを押す、「デバッグ」メニュー内の「続行」を選択する、または「F5」キーで、停止している箇所からプログラムを再開できます。
複数のブレークポイントを設定している場合は、次のブレークポイントで停止するまでコードが進みます。

プログラム実行を再開せずデバッグを終了させたい場合は、Visual Stuido側の「デバッグの停止」ボタンを押します。
(「デバッグ」メニュー内の「デバッグの停止」を選択、または「Shift + F5」キーでも良い)
デバッグの終了のアイコン

デバッグでプログラムが停止しているときに、デバッグ中のプログラムのウィンドウ(今回はコンソール画面)の閉じるボタン(右上の×ボタン)を押すと、Visual Studioがフリーズしてしまうことがあります。
そのため、必ず「デバッグの停止」でデバッグ中のプログラムを終了させてください。

ステップインとステップオーバー

ステップ実行にはステップインとステップオーバーの二種類があります。
それぞれの違いは、実行しようとしている行に関数が存在する場合です。

#include <stdio.h>

int add(int seisuu1, int seisuu2)
{
    int modori;
    modori = seisuu1 + seisuu2;
    return modori;
}

int main()
{
    int kazu1, kazu2, kekka;
    kazu1 = 3;
    kazu2 = 4;
    kekka = add(kazu1, kazu2);
    printf("計算結果: %d", kekka);
    getchar();
}

このコードの15行目、自作関数addを呼び出す行にブレークポイントを設定し、ステップイン実行とステップオーバー実行とを試してみましょう。

ステップオーバー実行の場合は、そのまま16行目に処理が移動します。
変数kekkaには、関数の実行結果の戻り値が代入されます。

対してステップイン実行の場合は、4行目に処理が移ります。
ステップイン実行

ステップイン実行は、関数があるとその関数の中に処理が移る実行方法です。
関数の戻り値が期待通りでない場合など、関数の処理の記述ミスが疑われる場合にはステップイン実行で関数の動作を一行ずつ検証してバグを見つけます。

このまま関数の終わりまでステップ実行をしてもいいですし、その関数の終わりまで一気に処理を進めたい場合はステップアウトを実行します。
「デバッグ」メニューから選択するほか、「Shift + F11」キーでもステップアウトを実行できます。
ステップアウトアイコン

一行に複数の関数がある場合

例えば以下のコードで15行目をステップインで実行すると、関数addに処理が移ります。
関数addを抜けると、15行目に処理が戻ります。
そこで再度ステップインで実行すると、再度関数addに処理が移ります。
15行目には、関数addの呼び出しが二回あるためです。

#include <stdio.h>

int add(int seisuu1, int seisuu2)
{
    int modori;
    modori = seisuu1 + seisuu2;
    return modori;
}

int main()
{
    int kazu1, kazu2, kekka;
    kazu1 = 3;
    kazu2 = 4;
    kekka = add(add(1, 2), add(3, 4));
    printf("計算結果: %d", kekka);
    getchar();
}

C言語組み込みの関数のステップイン実行

C言語が用意している関数、例えばprintf関数内にもステップイン実行で処理を移すことが可能です。
例えばprintf関数をステップイン実行すると以下のようになります。
printf関数にステップイン実行

テキストエディタの右上を見ると、「stdio.h」というファイルが開かれていることがわかります。
いつもサンプルコードの先頭で記述していた「#include <stdio.h>」のstdio.hです。
「#include <stdio.h>」は、printf関数を使用するために記述していたわけです。

C言語の組み込み関数の中身は、相当プログラミングに熟練しない限り覗いたり理解したりする必要はありません。
むしろ、下手にファイルを編集してしまうと動作がおかしくなるおそれがあるので、間違えてステップイン実行で開いてしまった場合はすぐに処理を自分のコードに戻してファイルを閉じてしまいましょう。