0

なぜこれがクラッシュするのですか?malloc()がコンストラクターを呼び出さないことがわかったので、手動で呼び出しましたが、それでもクラッシュします。理由がわかりません。

PS。std::vectorとnew[]が存在することは知っています。答えとしてvectors/new[]を使用するように言わないでください。

struct MyStruct {
    vector<int> list;
};
void make_crash(){
    MyStruct *array = (MyStruct *)malloc(100*sizeof(MyStruct));
    MyStruct element; // initialize element here since malloc() doesnt do it.
    array[0] = element; // copy, everything should be alright?
    array[0].list.push_back(1337); // nope, BANG!
    // The above line makes these:
    // First-chance exception at 0x7c970441 in test.exe: 0xC0000005: Access violation reading location 0xbaadf005.
    // First-chance exception at 0x00401cd0 in test.exe: 0xC0000005: Access violation reading location 0xbaadf00d.
    // Unhandled exception at 0x00401cd0 in test.exe: 0xC0000005: Access violation reading location 0xbaadf00d.
}
4

4 に答える 4

7

array[0] = element;のを呼び出している行operator=array[0]。は初期化されていないためarray[0]、これは未定義の動作です。operator=コンストラクターが呼び出されていないオブジェクトを含め、メソッドまたは演算子を呼び出すことは、未定義の動作です。

問題を修正するには、placement newを使用してのコンストラクターを呼び出すarray[0]か、のnew代わりにを使用する必要がありmallocます。を使用する正当な理由がない限りmalloc、後者の方が非常に望ましいです(または、さらに良いのは、ベクトルを使用することです)。

于 2012-06-01T21:14:11.907 に答える
4

That's not how you initialize an element in-place. The (implicitly created) assignment operator (which calls vector's assignment operator) is being called on an object that doesn't exist which is obviously bad news.

You have to use placement new instead:

new (array) MyStruct;

For arrays:

new (array) MyStruct[100];

于 2012-06-01T21:14:32.307 に答える
1
MyStruct *array = (MyStruct *)malloc(100*sizeof(MyStruct));

これはあなたが間違っているところです。

array指定した型に関係なく、 1 つ以上のMyStructオブジェクトへのポインターではありません。からの戻り値はmallocですvoid*。C++ の規則では、avoid*から他の型に暗黙的にキャストすることは許可されていません。そのため(MyStruct*)、そこにキャストする必要がありました。明示的なキャストが必要なだけで、怪しげなことをしていることがわかります。

C++ の規則では、 a を (特定の特殊な型以外の) 明示的にキャストする場合void*Type*これは、キャストを行っている がもともと aであり、それ自体が にキャストされていた場合にのみ有効です。これは原因ではありません。これは に由来し、決して ではありませんでした。あなたはコンパイラに嘘をついているので、未定義の動作を引き起こします。したがって、クラッシュします。void*Type*void*void*mallocMyStruct*

定義された動作が必要な場合は、この「C++ ではないなんて信じられない」言語ではなく、実際に C++ を使用する必要があります。例えば:

void *block = malloc(100 * sizeof(MyStruct));
MyStruct* array = new(block) MyStruct[100];

ここではキャスト操作がまったくないことに注意してください。

もちろん、この配列を削除するのは面倒です:

for(int i = 99; i >= 0; --i)
  array[i].~MyStruct();

free(block);

それらが構築されたときとは逆の順序で、逆方向に破棄する必要があることに注意してください。


これらが内部でどのように機能するのか疑問に思っています。new[] は、私の CPU に malloc ではないことを伝える追加のメモリを作成しますか? そして、 new[] なしでそれを模倣する理由/何/どのように?または新しい配置?CPUの生のバイナリコードでどのように書くのですか? それは可能ですか?ここで new[] が行う魔法とは正確には何ですか?

そのすべては実装に依存します。言語に関する限り、正確に何が起こるかは明確に定義されています。Placement new は、とりわけ、オブジェクトのコンストラクターを呼び出します。配列配置 new は、配列内のすべてのオブジェクトのコンストラクターを最初から最後まで順に呼び出します。それらのいずれかがスローされると、以前に構築されたオブジェクトのデストラクタが呼び出され、例外が発行されます。

正確に何が行われているかは実装に依存し、継承がどのように実装されているかなどと同じです。明らかに、コンパイラはそのためにコードを発行しますが、やはり、発行されるものは実装に依存します。

実際にC++を書いている限り、「CPU用の生のバイナリコードで書く」方法は不可能です。配置を新しく実装するには、クラスのコンストラクターを呼び出せる必要があります。そして...まあ、それが新しい配置の目的です。コンストラクターへのメンバー ポインターでさえも取得することはできません (取得できたとしても、メンバー ポインターの内容は実装に依存し、アセンブリ関数へのネイキッド ポインターになるとは限りません)。そのため、プラットフォーム固有の陪審員の談合なしに、コンストラクター コードを識別する方法さえありません。

生成されたアセンブリで配置 new の呼び出しを確認することで、特定のシステムでこれを行う方法を学ぶことができます。ただし、コンパイラごとに異なります。

于 2012-06-01T22:58:39.513 に答える
1

に割り当てると、MyStruct

array[0] = element;

最初に構造体の古いメンバーを破棄しようとしますが、構築されていないため、何もありません。ブーム!

100MyStruct秒を取得する最も簡単な方法は、別のベクトルを使用することです

vector<MyStruct>  v(100);

使用する必要はありませんmalloc

于 2012-06-01T21:21:54.340 に答える