拒否する! !迷信的なデバッグ
小さなディレクトリ
1. まず小さな概念を見てみましょう。
1.1のデバッグ?デバッグとは何ですか?
调试(debugging / debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。基本步骤如下:
- 発見プログラム エラーの存在 (プログラマー自身、テスター、ユーザー)
- エラーを切り分けて取り除く場所を特定する – バグを報告する (テスター)
- エラーの原因を特定する理由
- エラーを修正する方法を提案する解決策
- プログラム エラーを修正して再テストします
1.2 デバッグバージョンとリリースバージョンとは何ですか?
debug - デバッグ バージョン: 最適化されていないデバッグ情報が含まれており、プログラマがプログラムをデバッグしやすくなります。
release - リリース バージョン: ユーザーが使いやすいように、コード サイズと実行速度の点でプログラムを最適化するために、さまざまな最適化が行われています。
知らせ:
- 関連するバージョン情報は、フォルダー内をクリックして exe ファイルを開くと表示できます。
- リリース バージョンでは段階的にデバッグできません。同じコードのリリース バージョンの exe プログラムはデバッグ バージョンよりも大きくなります。
- Windows 環境でのデバッグの準備をするには、「デバッグ」環境を選択します
2. Windows環境でのデバッグスキル
2.1 「ショートカットキー」を正しく使いましょう
Fn - 二次ファンクション キー
- F5 – プログラムを開始します。多くの場合、(論理的な) 次のブレークポイントに直接ジャンプするために使用されます。
- Ctrl+F5 – 実行を開始します (デバッグなし)
- Shift+F5 – デバッグを停止します
- ctrl+shift+F5 – デバッグの再開
- F9 - ブレークポイントの設定/キャンセル (プログラム内のどこにでも可能)。多くの場合 F5 と組み合わせて使用されます。これにより、プログラムは目的の位置で実行を停止し、ステップごとに実行できます。
- ctrl+F9 – ブレークポイントを停止します
- ctrl+shift+F9 – すべてのブレークポイントを削除します
- ヒント:
条件付きブレークポイント (ループ内でブレークポイントの位置を設定): ブレークポイントを右クリック –> [条件] –> 対応する調整を行います
- F10 – プロセスごと、一度に 1 つのプロセス (1 つのステートメント、1 つの関数呼び出し)
- F11 - プロセスごとに処理し、一度に 1 つのステートメントを処理し、関数に入ることができます (最も一般的に使用されます)
2.2 デバッグ中にプログラムの現在の情報を確認する
操作步骤:调试开始 ->“调试”->“窗口”->“自动窗口/局部变量/监视/内存/调用堆栈”->“ ”->“ ”
2.2.1 一時変数の値を表示する
デバッグ後、これを使用して一時変数の値を表示します
操作手順:デバッグ開始→「デバッグ」→「ウィンドウ」→「自動ウィンドウ/ローカル変数/モニタリング/メモリ/コールスタック」→…
監視: 監視する変数情報を手動で設定します (正当な表現であれば監視できます 関数内の連続配列を監視します (アドレスを渡す): モニターに -> 「配列名、監視する要素の数」と入力します。。 デバッグと観察がさらに便利)、プログラムの実行を切り離しても消えません。
自動ウィンドウ:プログラム実行中に表示される現在の変数情報を観察のために自動的に表示します。
ローカル変数: プログラム実行中にコンテキスト環境のローカル変数情報を監視のために自動的に表示します。
2.2.2 メモリ情報の表示
メモリ: アドレス/配列名を入力すると、実際にメモリに格納されているデータが 16 進数で表示されます。
2.2.3 コールスタックを表示する:
コール スタック: 関数の呼び出しロジックをフィードバックします。
(ここでのスタックはデータ構造内のスタックです)
2.2.3 ビューの逆アセンブリ
デバッグ後、右クリックのオプションに逆アセンブリ オプションもあります。
2.2.3 レジスタの表示
方法 1: デバッグ後、[ウィンドウ] でレジスタを選択します
方法 2: デバッグ後、監視するレジスタの名前を [モニター] ウィンドウに入力します。あ>
3. !!!古典的なデバッグ例!!!
次のように、単純だが奇妙なコードをリリースします。
int main()
{
int i = 0;
int arr[10] = {
1,2,3,4,5,6,7,8,9,10 };
for (i = 0; i <= 12; i++)
{
arr[i] = 0;
printf("hehe\n");
}
return 0;
}
非常に混乱している学生もいると思います。コンパイラによって検出されない可能性のある範囲外アクセスを除いて、このコードにバグがある場合、何がそんなに奇妙なのでしょうか?以下は、vs2019 で実行した後のインターフェイスで、誰でも見ることができます:
コンパイラによって境界外アクセス エラーは検出されませんでしたが、プログラムは無限ループに入りました...
では、この目に見えないバグの原因は一体何でしょうか?デバッグを使用して問題をトラブルシューティングします。
- デバッグを開始し、監視をオンにして、「名前」フィールドに表示されるすべての要素を入力します。
- 定数 F10 では、プログラムは問題なく i=12 に達します。
- プログラムが arr[i]=0 を通過するまで
、i の値は元々 12 でしたが、arr[i]=0; を代入した後は 0 になりました。その結果、i の値は 12 を超えることはできず、条件が満たされず、ループを終了できません。 - 鋭い観察力を持つ学生は、モニタリングにおける i と arr[12] の値が同じであることを発見しました。12 番目のループがデバッグされていない場合でも、arr[12] は i によって変化します。 i の値を変更するために arr[12] にアクセスした後、監視に i と arr[12] のアドレスを追加して次のことを確認します。
アドレスは実際には同じです...これもarr[i ] が常に i の値と一致する理由を説明します。 - デバッグ – バグの原因を突き止めるこのステップは完了です。
这其实是一道笔试题,也在《C陷阱和缺陷》一书中写出来了,具体的原因,笔者用模仿笔试答题的方法为同学们解答:
- スタック領域メモリの使用習慣は、最初に上位アドレスの空間を使用し、次に下位アドレスの空間を使用します。
- 配列添字が増加すると、アドレスは低位から高位に変化します
- i と arr の間に適切なスペースがある場合、配列の範囲外操作を使用すると i が上書きされる可能性があり、死に至る サイクルの出現
- ここで区切られた 2 つのスペースは環境とコンパイラに関連しています。
4. まとめ
優れたコードを作成するには、優れたコード ロジック、デバッグ性、保守性が必要です。一般的なコーディング テクニックは次のとおりです。
- アサートを使用する
- constを使ってみる
- 適切なコーディングスタイルを開発する
- 必要なコメントを追加する
- コーディングの落とし穴を回避する