Visual C++のデバック編です
Visual C++のデバック機能は非常に優秀で使いこなせば
大抵のバグの箇所を発見することができます

特にC/C++言語では直接変数のアドレスを(ポインタ変数として)取り扱うことができるので
(他の言語にも参照というポインタに近い概念はある)
デバックするときは変数の値のチェックやアドレスのチェックが必須となります

まず、空のプロジェクトから
テスト用にmain.cppを作ります
デバックモードならばアドレスとかの関係も簡単に見れるので
今回はswap関数の例にしておきます

	#include <stdio.h>
 
	// 値入れ替え関数(駄目な例)
	void swap1(int a,int b)
	{
		int tmp = a;
		a = b;
		b = tmp;
	}
 
	// 値入れ替え関数(OKな例)
	void swap2(int* a,int* b)
	{
		int tmp = *a;
		*a = *b;
		*b = tmp;
	}
 
	int main()
	{
		int a = 5;
		int b = 10;
 
		printf("a = %d,b = %d\n",a,b);
		swap1(a,b);
		printf("a = %d,b = %d\n",a,b);
		swap2(&a,&b);
		printf("a = %d,b = %d\n",a,b);
	}

先に実行結果は、次のようになります
swap1関数では変数a,bの値が変わっていませんが
swap2関数では変数a,bの値が無事入れ替わっています
アドレスの概念の復習がてら、デバックで見ていきましょう

&ref(debug13.GIF);

デバック起動するときは
Debugモードにしておきます
Releaseモードだと正しくデバックできません

&ref(debug.GIF);

次にソースコードの左枠の部分を左クリックします
すると赤点(ブレークポイント)が打たれます
デバック起動時、このブレークポイントのところで処理が止まります

&ref(debug1.GIF);

それでは、デバック起動してみます
デバック起動をするには、
デバック→デバック開始を選ぶか、もしくはF5キーを押してください

&ref(debug2.GIF);

すると、赤点(ブレークポイント)を打ったところで処理が止まります(黄色い矢印)

&ref(debug3.GIF);

呼び出し履歴のウィンドウはどの関数から呼ばれたかの履歴を示しています
自動変数は、今止まっている個所での変数の値をチェックすることができます

ない場合は、デバック→ウィンドウからそれぞれ出すことができます

&ref(debug4.GIF);

デバックの進行方法ですが、主に次の5つです
・進行(F5キー):次のデバックポイントまで移ります
・デバックの中止(Shift + F5キー):デバックを中止します
・ステップイン(F11キー):一行進みます、関数の行の場合、関数の中身に入ります
・ステップオーバー(F10キー):一行進みます、関数の行の場合、関数の中身に入らず飛ばします
・ステップアウト(Shift + F11キー):関数の中に入っている場合、関数から抜けます

&ref(debug5.GIF);

試しに、今の状態からF10キーを押します(ステップオーバー)
すると、aに5が代入され、
自動変数のaの値が5になっていることが確認できます
また、変数bが宣言されたのでbも自動変数の欄に登場します
bはまだ初期化されていないのでゴミが入っています
このように、自動変数のウィンドウは現時点で有効な変数のみを表示します

&ref(debug6.GIF);

とりあえず、
swap1までF10キーを押します(ステップオーバー)

&ref(debug7.GIF);

変数aとbのアドレスを見たいので
タブ切り替えでウォッチ式のウィンドウを出します
(自動変数のウィンドウでは見れない)

&ref(debug8.GIF);

ウォッチ式がない場合はデバック→ウィンドウ→ウォッチ1から出せます
番号はあまり関係ありません

&ref(debug9.GIF);

ウォッチ式の欄で次のように
左クリックして、入力してエンターキーを押します

&ref(debug10.GIF);

bも同様に行うと、aとbのアドレスは次のようになります

&ref(debug11.GIF);

この値は人によって違うと思います
私の場合は
aのアドレスは0x0012ff60
bのアドレスは0x0012ff54
になりました
このアドレスはあとでチェックするのでメモって置いてください

それではF11キーを押して(ステップイン)、swap1関数の中身に入ります
&ref(debug12.GIF);

ここでいくつか見てほしい部分があります
まず、呼び出し履歴の欄です
swap1が追加されています
この下にmain関数の記述が書かれているので
このswap1はmain関数から呼び出されたものとわかります
プログラムが複雑になってきてバグが発生すると
どの関数から呼ばれてバグが発生したのか、
また、その時の変数の値などがバグの原因を突き止めるため大事になります

また、変数のアドレスを見てください
私の場合は
aのアドレスは0x0012fe7c
bのアドレスは0x0012fe80
となりましたが
先ほどのmain関数の変数a,bのアドレスと異なるはずです

試しにa,bのウォッチ式を追加します

&ref(debug14.GIF);

swap1関数の変数(引数)a,bにデータは確かにそれぞれコピーされていますが
main関数の変数a,bのアドレスとswap1関数内の変数(引数)a,bのアドレスは異なるので
main関数の変数a,bに影響を与えません
つまり、swap1は無駄な処理であることがこれでわかったはずです

では、Shift+F11を押して(ステップアウト)、swap1関数を抜けましょう

&ref(debug15.GIF);

デバック起動中にもブレークポイントを打つことができます
試しに、次の箇所にブレークポイントを打ってみましょう

&ref(debug16.jpg);

F5を押してswap2関数に行きます
ここでswap関数には、main関数の変数aとbのアドレスを引数として渡します
さらにF5を押して、swap2関数に入ると次のようになります

&ref(debug17.GIF);

ここでswap2関数のポインタ変数(引数)a,bの値にはmain関数の変数a,bのアドレスがコピーされています
確認してみてください
私の場合は
aの値は0x0012ff60
bの値は0x0012ff54
となり、main関数の変数a,bのアドレスと一致しました

やっていることはアドレスの値をコピーしているにすぎないわけです
ポインタ変数はアドレスを値として格納する変数なのでアドレスのコピーが可能になるわけです
次にウォッチ式に*a,*bの式を追加します

&ref(debug18.GIF);

 *(アドレス)
は間接参照演算子とよばれ、
アドレスの指す先の値にアクセス(参照)することができます
もちろん、swap2関数のポインタ変数(引数)a,bの値はmain関数の変数a,bのアドレスそのものなので
その先の指すものは、main関数の変数a,bの値です
なのでswap2関数で*a,*bの値の入れ替えを行うことは直接main関数の変数a,bの入れ替えを行ってるのと同じです

この項目を通してデバックのやり方と、
アドレスの概念が身に着いたと思います

慣れてくるとプログラムが落ちたときなど
デバック起動でバグの原因を究明し、
バグをなくせるようになると思います
積極的に使っていきましょう

#vote((^ω^)やったお[0],何これwww意味不すぎwww[0],。(`ω´#)。あぁん?最近、だらしねぇな[0])
|(^ω^)やったお|3|
|何これwww意味不すぎwww|0|
|。(`ω´#)。あぁん?最近、だらしねぇな|0|


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS