2009-04-08 12 views
2

私はクラスを持っています。C. Cには以下のように宣言されたメンバ変数があります。sizeof(* this)は間違った値を返します

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

C内の他の場所では、次のようにします。markerStart = false;

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

逆アセンブルコードを見てみると、私は見つける:

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

二移動命令がゼロにこの+ 0x218でバイトを設定しているが、クラスだけ0x216バイト長であることから、これはメモリをつかうれます!

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

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

誰でも私にこの問題を解決するために探し始めるための手がかりを与えることができますか?

更新:すべてのヘルプありがとうございます。 コードなしでは、あなたの誰かが問題を解決することは不可能でした。私が探していたのは、どこから探し始めるかのヒントでした。あなたの大部分は素晴らしいヒントを提供しました。ありがとうございます!

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

+1

問題を説明する実際のC++コードを投稿してください –

+0

実際のコードを投稿できますか? Cの定義は良いスタートになるでしょう – PaulJWilliams

+0

おそらく218!= 0x218 – Skizz

答えて

5

として、あなたはおそらく何らかの形で一つの定義ルールを破りました。 2つの翻訳単位で、おそらく前のヘッダーファイル内の他のコードが、クラスCの定義が2つの翻訳で異なるサイズになるように解釈される方法を変更するクラスCの定義を含むヘッダーファイルをおそらく含まれています単位。

2つの異なる翻訳単位で、構造体のサイズが異なると思われるように、デフォルトのメンバーの整列(コンパイラのコマンドライン引数またはソースの#pragmaを介して)を誤って変更した可能性がありますパディングの量が異なるため(ほとんどのx86コンパイラでは、デフォルトで4バイト境界のアラインメントが必要ですが、必要に応じて1バイト境界でアラインメントを要求できます)。他のヘッダーファイルを参照して#pragmaのデフォルトのアラインメントを変更し、#pramaを元の値に戻します(コンパイラを指定しないので、指定できません)。

+0

ニースのスティーブン。私はあなたの答えを読む前に問題を見つけましたが、あなたは頭の爪に当たったのです。乾杯! – Rob

0

クラスは0x216バイトしか持たないかもしれませんが、次のオブジェクトはもちろん最初のオブジェクトの開始後0x218バイトです。あなたのオブジェクトは明らかに4バイトのメモリ境界に整列しています。これはデフォルトです。

あなたの記憶がどこに詰まっているかを知るには、別の場所を見る必要があります。間違いなく 'markerStart = false'命令です。

+0

生成コード[eax + 218]はどうですか?オブジェクトが16バイトの境界に整列されていない限り、次のオブジェクトは上書きされます。 – Mike

+0

とにかく意味がありません。オブジェクト間にパディングはありません。(char *)(a + n)==(char *)(a)+ N * sizeof(a)。 sizeof(C)が0x216の場合、コンパイラはCオブジェクトを任意の偶数アドレスに配置できます。クラスのアラインメントがすべてのメンバーアライメントのGCDになるのはかなり一般的です。 2はOKと思われます。 – MSalters

7

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

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

  1. はあなたが興味のあるメンバ変数をシャドウ別markerStart変数を参照している
  2. あなたはCの基底クラスのメソッドにはsizeofを計算します。 sizeof()は、動的型ではなく、静的型のみを測定します。
  3. One Definition Ruleが壊れていて、クラスCの2つの異なるバージョンを持っています(ヘッダーファイルのいくつかの#ifdefを介して2つの翻訳単位で異なる解釈が行われている可能性があります)。

より多くの情報がなければ、私はODR違反のために行くと思います。これらは潜在的であり、コンパイルやリンク時に検出することは不可能です。

+0

あなたはオプション2について正しいです。私はsizeof(* this)をVSウォッチウィンドウで直接計算して0x216を取得しています。 Cのメソッドにコードを入れると、sizeof(* this)は0x220を返します。これはまだ[eax + 218]が次のクラスを壊している理由を説明していません! ありがとう、Rob – Rob

+0

IDEのように聞こえることも、何が起こっているのか混乱しています。もし私があなただったら、私はまだODR違反(オプション3)を長い目で見ています。複数の.hファイル?直接#ifdefs?間接的な#ifdefの依存関係は、メンバ変数の宣言を通してですか?他のマクロトリッキー? –

+0

ODR違反と同様に、私は、オブジェクトファイルの1つを不適切に再構築して最終的な実行ファイルの内部で不一致のクラスになるというまれなケースを見ました。 – Mike

0

これは「クラススライシング」問題のインスタンスですか?

1

クラスには仮想メソッドがありますか?またはそれは仮想メソッドを持つクラスから派生していますか?あるいはあなたのクラスは複数の継承を持っていますか?

答えはあなたが使用しているコンパイラによって異なり、コンパイラは、仮想テーブル(複数可)へのポインタ(複数可)を保存することができます。これは実装の詳細ですが、標準と同じように動作する限り、各オブジェクトに任意の種類のデータを格納できます。

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()を印刷できますか(クラスにいくつかの仮想関数があるとします)?

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:あなたは上記の2つのファイルをコンパイルし、それらをリンクした場合

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

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

は、あなたが任意のエラーや警告を取得することはできません。しかし、アプリケーションを実行すると、その引数がarray_of_c [0]であっても、DoSomethingWithC clobbers array_of_c [1]が見つかります。

だからあなたの問題は、一つのソースファイルは、クラス1つの方法を見て、別のファイルはそれを別の方法を見ている可能性があります。これは、依存性検査が失敗した場合に発生します。

リビルドをすべて強制してください。それがうまくいくならば、依存関係がなぜ失敗したのかを知る必要があります(例えばDevStudioは時々間違ってしまうことがあります)。ポントスによって指摘

1

最近、クラス定義やプロジェクト設定が変更されていますか?私はこのような問題を抱えていました。私はこのような振る舞いを絶対にやっていませんでした。オブジェクトファイルはソースファイルと一致しなくなりました。清潔で完全な再構築を試みてください。

0

*これは実際には悪いポインタ経由の呼び出しではなく、クラスの開始点を指していることを確認してください。

関連する問題