0

Visual Studio 2012 RC を使用して一部の C++ コードをデバッグしているときに、クラス メンバー関数とメンバー変数の値にブレーク ポイントがある奇妙な動作に気付きました。

クラス メンバー関数にブレーク ポイントを設定すると、VS 2012 は関数の中かっこにブレーク ポイントを配置します。これで、関数で使用されるメンバー変数にカーソルを合わせると、値は常に「初期化されていません」になります。ただし、F10 キーを押して次の行 (関数の最初の行) に進むと、メンバー変数が正しい値に変更されるようになりました。

これは、メンバー関数の最初の行に入るまで、クラスのメンバー変数の値が読み込まれないように見えます。しかし、これは非常に紛らわしいです - 中括弧を壊すポイントは何でしょうか? (関数にブレークポイントを設定するときのデフォルトです。)

クラスメンバー関数の中断:

ブラケットの破損

メンバー変数activetrue(実際にはそうではありません!)

F10 で次の行に進む:

ここに画像の説明を入力

メンバー変数は(正しい) とactive呼ばれるようになりました。false

ここに何か不足していますか、それとも Visual Studio 2012 RC の実際のバグですか?

編集: Visual Studio 2010 のコピーを見つけて、これを試しました。ブレークポイントに関する動作は同じです。違いは、IntelliSense が状況を処理する方法にあります。VS 2010 では、IntelliSense は中括弧を壊してもツールヒントをまったくポップアップしません。VS 2012 ではツールヒントが常に表示されます。混乱を避けるために、VS 2010 の動作の方がはるかに優れていると思います。

4

2 に答える 2

1

x86 の既定では、C++ メンバー関数は thiscall 呼び出し規約(*)を使用します。これは、レジスターthisを介してポインターを渡します。ただし、関数はこのレジスタを計算に使用する場合があるため、デバッガは、関数の実行中ずっとその値がポインタであるecxことに依存できません。this

したがって、最適化されていないビルドでは、関数の本体に入る前に実行される関数のプロローグの一部として、thisポインターがスタックに「スピル」されます。デバッガーができるように、ポインターはスタック上の既知のオフセットにコピーされます。その値を確実に取得します。thisウォッチするときにデバッガーが使用するのは、このポインターのコピー、thisまたはメンバー変数 (ポインターを介して暗黙的にアクセスされるthis) です。

の左中かっこに{ブレークポイントを配置すると、ブレークポイントは関数プロローグの最初のアドレス、つまり、関数が呼び出されたときに実行される最初の命令に配置されます。this関数プロローグが実行され、ポインターがスタックにスピルされるのは 、この左中かっこをステップ オーバーした後でのみです。

これは、関数のプロローグを通してデバッグする必要がある場合に役立ちます。逆アセンブル ([デバッグ] -> [Windows] -> [逆アセンブル]) を実行すると、何が起こっているのかがより明確になります。


(*) x64 では、呼び出し規則は 1 つだけで、fastcall 呼び出し規則です。thisポインタは、関数の「最初の」引数であるため、レジスタを介して渡されますrcx

x86 では、すべてのメンバー関数が thiscall 呼び出し規約を使用しているわけではありません。これは単なる既定値です。他の関数タイプと同様に、関数の呼び出し規約を指定できます。これは、たとえば、stdcall 呼び出し規則を使用する COM コンポーネントに対して行われます。

于 2012-08-05T21:10:41.087 に答える
1

中括弧にブレークポイントを設定することで、デバッガーに岩と難しい場所のどちらかを選択させることができます。難しいところの 1 つは、コーディング スタイルの規則です。K&R ブレーシングは好きですか?

void foo() {
   // etc..
}

そのブレークポイントをどのように設定しますか? デバッガーは安全側でエラーを起こし、関数のエントリ ポイントにブレークポイントを設定します。ブレークポイントがヒットしたら、Debug + Windows + Assembly を使用できます。関数内の最初のマシン コード命令にブレークポイントが設定されていることがわかります。ほとんど常にpush ebp。これは実際には、デバッガがあいまいなブレークポイントを処理しようとする通常の方法とは少し異なります。通常、デバッガは後方ではなく前方を見ます。したがって、これは非常に意図的に行われました。マネージ コードのデバッガーで対処されたのは、行ベースだけでなく、列にも注意を払うことです。これは C++ デバッガーには反映されず、依然として行ベースです。

したがって、ローカル変数の検査はうまくいきません。何もありません。スタック フレームを設定する関数プロローグが実行されるまでは。これを含む。

デバッガーが通常のように前方ではなく後方を検索する理由として考えられるのは、ローカル変数であるクラス オブジェクトのコンストラクターをシングル ステップで処理したいということです。繰り返しますが、スタックを自分で巻き戻し、コンストラクターにブレークポイントを設定する以外に、明確なブレークポイントを設定できるものではありません。誰がどこを知っていますか。

機能であり、バグではありません。明らかな回避策は、関数本体の最初のステートメントにブレークポイントを設定することです。それまでに、スタックフレームとローカル変数のすべてがセットアップされます。

于 2012-08-05T21:48:03.143 に答える