#pragma twice

KAB-studio > プログラミング > #pragma twice > 108 Version 6.08 バグも色々

#pragma twice 108 Version 6.08 バグも色々

前のページへ 表紙・目次へ 次のページへ

 Version 6.08
バグも色々

前回までで、コンパイルエラーの取り除き方を解説しました
っつーか、前半は〈もっと C 言語知らないとね〉って話で、後半はプリ
プロセッサとマクロの話だったじゃん
う”……まぁ、そういう部分はこれから少しずつ教えることにして、今回
からはいよいよ〈バグを取り除く〉ってことについて見ていきましょう
そうそう、コンパイルエラーはバグって言わないんだよね
そう、実行している間に起きる問題が〈バグ〉。そして、そのバグを取り
除くことを〈デバッグ〉と言います
これは常識よね
バグと一口に言っても、いろんな種類があります。 Ver 6.01 ( No.101 ) 
の復習を兼ねて、バグの種類についてまず見ておきましょう
はーい! まずはランタイムエラー
実行中に、イヤーなダイアログが出ること、それがランタイムエラーでし
た。たとえば

void CDebugDlg::OnButton1() 
{
    int *pi = NULL;
    *pi = 100;
}

無理矢理 0 番地にアクセスするってゆーとんでもないの
これをすると、 VC からデバッグ環境で実行した場合には

ハンドルされていない例外は Debug.exe にあります 
    0xC0000005: Access Violation

というダイアログが表示されます。同時に、アウトプットウィンドウに

例外処理 (初回) は Debug.exe にあります: 0xC0000005: Access Violation

ってメッセージが出力されます
フツーに実行したときには

このプログラムは不正な処理を行ったので強制終了されます。
終了しない場合は、プログラムの製造元に連絡してください。

っておなじみのダイアログが出るんだよね
そうでした。まず最初の、 VC から実行した方のメッセージを見て。〈例
外〉って出てるでしょ
うん、出てる
ウィンドウズでは、無茶苦茶なことをされると〈例外が投げられる〉んで

例外ってゆーのが投げられる?
イメージとしては、ボクシングでセコンドがタオルを投げ入れる感じか

あー! もうダメだっ! って感じで投げて、そうするとその試合は負け
になるんだよね
まずは、ランタイムエラーは例外が投げられたってことだってことを憶え
ておいて。これがあとで重要になるから
ふーん
もうひとつ、普通に実行したときのダイアログについて
【デバッグ】と【詳細】ってボタンがあるけど、デバッグは VC から実行
したのと同じ状態になるんだよね
そう、 VC がプログラムを管理して、バグがどこか突き止めることができ
るようになります
それって結構すごくない?
すごくないです。普通に売ってるアプリでこれをしても、せいぜい原因が
分かるくらい、しかも解決方法が分かるとは限らないんだよね
なーんだ。そいじゃ、【詳細】の方は?
【詳細】を見ると、こういうふうに出ています

DEBUG のページ違反です。
モジュール : DEBUG.EXE、アドレス : 015f:00401d42
Registers:
EAX=00000000 CS=015f EIP=00401d42 EFLGS=00010206
EBX=0064f860 SS=0167 ESP=0064f454 EBP=0064f4a4
ECX=00000000 DS=0167 ESI=005201c0 FS=4817
EDX=00000000 ES=0167 EDI=0064f4a4 GS=0000
Bytes at CS:EIP:
c7 00 64 00 00 00 5f 5e 5b 8b e5 5d c3 cc cc cc 
Stack dump:
0064f4fc 005201c0 0064f860 cccccccc cccccccc cccccccc 
cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc 
cccccccc cccccccc cccccccc cccccccc 

ううっ、頭がクラクラする……
これは、ランタイムエラーが出たときの CPU やメモリの状態が出てるん
だけど、はっきし言ってほとんど役に立ちません
うお、そうなの?
ただ〈モジュール〉と〈アドレス〉はちょっと重要
モジュールはエラーの出たファイルだよね。アドレスって、メモリのアド
レス?
そう。ちょっと難しくなるから簡単にしか説明しないけど、プログラムも
コンピューターのものだから、メモリ上に置かれます
前に少しやったね
そこで、 VC から実行してランタイムエラー起こして、メニューの
【表示】−【デバッグ ウィンドウ】−【混合モード】を選んで
初めてのウィンドウだね。うりゃ

177:      int *pi = NULL;
00401D38 C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0
178:      *pi = 100;
00401D3F 8B 45 FC             mov         eax,dword ptr [ebp-4]
00401D42 C7 00 64 00 00 00    mov         dword ptr [eax],64h
179:  }

うにゃー、もっと難しい!!
そんなことないよ。よく見てみて
ん……あ、 int *pi = NULL; とかって、プログラムそのものじゃん
この〈混合モード〉は、プログラムと、それがコンパイルされたあとのプ
ログラムを両方いっぺんに表示するものなんです
あー、だから混合モードなわけね。ってーと、 int *pi = NULL; ってー
のがコンパイルするとその下のわけ分かんないのになるわけだ
この表示で黄色矢印が 00401D42 に出てるでしょ。これは、ここの行でエ
ラーが発生したって意味
ほほう。あ、この左っかわの 00401d42 って数字、さっきの【詳細】ダイ
アログの〈アドレス〉のと同じだ!
でしょう。この左側の数字は、実際にプログラムが置かれたメモリのアド
レス。これとさっきのを照らし合わせれば
どこでエラーが起きたのか分かるわけねー
ま、こううまくいくとは限らないけど、こういうヒントもあるってこと
で。実際にデバッグするときには、こういう情報は使わないから
そりゃそうよね、 VC でやってたらプログラムのどこで落ちたか判るんだ
から
そうそう。さて、ここまではランタイムエラーを見てきたけど、実際には
それ以外にもバグがあります
この前言ってたのは、無限ループだよね

void CDebugDlg::OnButton1() 
{
    while( 1 )
    {
    }
}

無限ループは論外だけど、近い状態になることは結構多いんだよね
確かに、ファイル操作とかのアプリ使ったときに、ファイルのサイズが大
きかったりするとすんごく時間かかったりするね
ファイル操作や各種ハードへのアクセスは、その場の状況でかなり変わる
からね。普通の計算でも、 CPU やメモリでだいぶ変わるし
でもさ、そーゆーのはこっちじゃフォローしきれなくない?
フリーウェアとかならそういうのも許されるんだろうけど。パッケージア
プリならお金かけてフォローすべき、だね
フリーウェアなら、逆にみんなに使ってもらって、それで環境チェックと
かできそうだけど
それにはユーザーの信頼を得ないとね……。あともうひとつ、バグの種類
があります
それもこの前言ってたね。プログラマーの思い通りじゃない動きはバグな
んだよね
厳密に言えば、ユーザーの思い通りに動かないってことになるんだけど、
その辺は微妙かも
う”−、そーゆー政治的っつーかグログロしたのはイヤ……
この手のバグは、他のバグと違って動くんだけど、意図したものと違う
つまり動くことは動く、ってことね
そゆこと。よっぽど致命的でない限り、ある程度許されるバグってところ
かな。内容にもよるんだろうけど
ちゃんと動いてても、ファイルセーブができない、っていうのは致命的だ
よね
それはそうだ
で、こういったバグを潰してけばいいわけだね
っと、その前に必要なことがあるんだよね
必要なこと?
そう、それは〈バグを見つける〉ってこと
へ?
たとえば上の無限ループ。もし CDebugDlg::OnButton1() がたった一度も
呼ばれなかったら、無限ループになってるって気付かないかもしれないで
しょ
つまり、バグがあっても出遭わなきゃ分かんない、ってこと?
そういうこと!
ってことは、出遭わなきゃバグがあってもいいってこと?
ちがーう!! そりゃ、絶対に出ないバグならいいよ、絶対に、なら
1%でも出る確率があれば?
1%って何が?
何がって……普通に使ってて、1%の確率でバグに出遭っちゃう
その1%も、状況によって大きく変わるでしょ。ドラクエやってボスまで
行ってフリーズしたらどうする!?
なんか水希ちゃん目がマジだよ?
あ、えーっと、つまり
つまり1%の確率でも、致命傷になるならダメってことね
そういうこと! そのために必要なのが、テストとゾーニング
テスト、は分かるけど、ゾーニングって?
 Zoning は範囲を絞る、ってこと。バグは予想外の事が起きることだか
ら、できるだけプログラムが〈予想範囲でしか動作しない〉ようにするんで

なんかいまいちイメージ掴めないけど
たとえば const か、そうじゃないか

void Use_non_const( char *p_pch )
{
//  実装。
}



void Use_const( const char *p_pch )
{
//  実装。
}

上が非 const 、下が const ね
非 const の方は、 p_pch に対して書き込みすることができます
下の const のは、 p_pch は読み取り専用だね
つまり!  const は p_pch の〈書き込める範囲〉を狭めたってこと
そっか、つまり const の方は〈値が書き変わる〉ってことを考えなくて
いいんだ。それがゾーニングってことなんだね
そゆこと。次回からは、そういう視点に立って、バグをいかに減らすか、
考えていきましょう

/*
    Preview Next Story!
*/
バグの話になると水希ちゃん熱いね
う〜ん、そういえばそうかも
でもなんで?
それこそ、一番大事なことだから、ね
というわけで次回
< Version 6.09 単項演算子 >
につづく!
終わるのに5年掛かってもいいくらい大事?
大事!!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。