2

C というクラスがあります。C には次のように宣言されたメンバー変数があります。

C 内から sizeof(*this) を呼び出すと、0x216 バイトの値が返されます。

C 内の他の場所では、markerStart = false; を行います。

この呼び出しは、markerStart を false に設定するのではなく、実際にはメモリ内の次のクラスの開始を破壊しています!

逆アセンブルされたコードを見ると、次のことがわかります。

markerStart = false;
06FB6B7F mov            eax, dword ptr [this]
06FB6B78 mov            byte ptr [eax+218h], 0

2 番目の移動命令は、この + 0x218 のバイトを 0 に設定していますが、クラスの長さが 0x216 バイトしかないため、これはメモリを破壊しています!

コメントに応じて、それは間違いなくmarkerStart = false命令です。逆アセンブラー ビューとメモリ ビュー (およびデータ ブレークポイントを使用して Windbg を使用) で発生する様子を見ることができます。次のクラスの最初のバイトがゼロに設定され、vftbl ポインタが台無しになります。

注:markerStart のアドレスを取得して、これから減算すると、0x211 が得られます。

この問題を解決するためにどこから始めるべきかについて、誰かが私に手がかりを与えることができますか?

更新: ご協力ありがとうございます。コードがなければ、この問題を解決することはほぼ不可能でした。私が探していたのは、どこから探し始めるかについてのヒントでした。あなたのほとんどは素晴らしいヒントを提供してくれました。ありがとうございます!

私はついに問題を見つけました。この場合、アラインメントは 1 つのクラスで設定されており、重要なコード ブロックの後に正しくリセットされていませんでした。クラス C の宣言の直前に、アラインメントに問題のあるクラスがたまたまコンパイルされたため、問題が発生しました。

4

9 に答える 9

7

より多くのコードを投稿する必要があります。異常が発生する最小限のクラス定義まで削減できればさらに良いでしょう。それ自体が、何が起こっているのかを特定するのにおそらく役立つでしょう。

私に起こるいくつかの可能性:

  1. 関心のあるメンバー変数をシャドウする別の markerStart 変数を参照しています。
  2. C の基本クラスのメソッドで sizeof を計算します。sizeof() は静的型のみを測定し、動的型は測定しません。
  3. どこかで1 つの定義ルールを破っており、クラス C の 2 つの異なるバージョンがあります (おそらく、2 つの翻訳単位で異なる解釈をするヘッダー ファイルの #ifdef による)。

これ以上の情報がなければ、ODR 違反に行きます。これらは潜行性があり、コンパイルまたはリンク時に検出することは不可能です。

于 2009-04-08T12:27:09.630 に答える
5

Pontus が指摘したように、おそらく何らかの形で 1 つの定義規則を破っています。クラス C の定義を含むヘッダー ファイルを 2 つの翻訳単位に含めた可能性があります。他のコード (多くの場合、前のヘッダー ファイル) によって、クラス C の定義の解釈方法が変更され、2 つの翻訳単位でサイズが異なります。ユニット。

1 つの可能性は、(コンパイラへのコマンド ライン引数またはソースの #pragma を介して) デフォルトのメンバー アラインメントを誤って変更したため、2 つの異なる翻訳単位が、量が異なるために構造体のサイズが異なると見なすことです。パディング (ほとんどの x86 コンパイラは、デフォルトで 4 バイト境界での位置合わせを行いますが、必要に応じて 1 バイト境界での位置合わせを要求できます)。他のヘッダー ファイルを調べて、次の #prama が欠落しているデフォルトの配置を変更する #pragma を調べて、以前の値に戻します (どのコンパイラーを指定していないため、詳細を示すことはできません)。

于 2009-04-08T14:27:17.750 に答える
1

あなたのクラスには仮想メソッドがありますか? それとも、仮想メソッドを持つクラスから派生していますか? または、クラスに多重継承がありますか?

答えは、使用しているコンパイラによって異なります。コンパイラは、仮想テーブルへのポインタを格納できます。これは実際には実装の詳細ですが、標準と同じように機能する限り、各オブジェクトであらゆる種類のデータを格納できます。

于 2009-04-08T12:53:44.417 に答える
1

あなたが遭遇している問題は、おそらくコンパイラの問題ではなく、いくつかの依存関係エラーによるものです (コンパイラは何十万もの開発者によって使用されており、このような問題がある場合は、今までに発見されていたはずです)。

次の 2 つのファイルを含む単純なプロジェクトを考えてみましょう。ファイル a.cpp:

class C
{
public:
  C () : m_value (42) { }
  void Print () { cout << "C::m_value = " << m_value << endl; }
private:
  int m_value;
};

void DoSomethingWithC (C &c);

void main (void)
{
  C array_of_c [2];
  DoSomethingWithC (array_of_c [0]);
  array_of_c [0].Print ();
  array_of_c [1].Print ();
}

およびファイル b.cpp:

class C
{
public:
  int a,b;
};

void DoSomethingWithC (C &c)
{
  c.b = 666;
}

上記の 2 つのファイルをコンパイルしてリンクすると、エラーや警告は表示されません。ただし、アプリケーションを実行すると、引数が array_of_c [0] であっても、DoSomethingWithC が array_of_c [1] を破壊することがわかります。

したがって、問題は、あるソース ファイルがクラスを 1 つの方法で認識し、別のファイルが別の方法でクラスを認識している可能性があります。これは、依存関係のチェックが失敗した場合に発生する可能性があります。

すべてを強制的に再構築してみてください。それが機能する場合は、依存関係が失敗した理由を確認する必要があります (たとえば、DevStudio は時々間違っている可能性があります)。

于 2009-04-08T13:51:23.250 に答える
1

コードに奇妙なポインター キャストの問題がありませんか? これに似た何か?

struct A
{
  int i;
};

struct B : public A
{
  int j;
  void f() { j=0; }
};

int main()
{
  A x;
  A* p=&x;
  ((B*)p)->f();
  return 0;
}

これが実際にメモリを破壊している行の C のインスタンスを指していることを確認できますか? typeid(*this).name()その時点で印刷できますか(クラスにいくつかの仮想関数があると仮定して)?

于 2009-04-08T13:05:14.170 に答える
0

*this が、不正なポインターによる呼び出しではなく、実際にクラスの開始を指していることを確認してください。

于 2009-04-08T16:54:17.413 に答える
0

クラスには 0x216 バイトしかない場合がありますが、次のオブジェクトはもちろん最初のオブジェクトの開始後 0x218 バイトです。オブジェクトは、デフォルトである 4 バイトのメモリ境界に配置されているようです。

どこで記憶が失われるかを調べるには、別の場所を探す必要があります。これは間違いなく「markerStart = false」命令ではありません。

于 2009-04-08T12:19:31.347 に答える
0

これは「クラスのスライス」の問題のインスタンスでしょうか?

于 2009-04-08T12:50:36.640 に答える