为什么push_back()会导致malloc()的ed数据崩溃?

时间:2012-06-01 21:11:44

标签: c++ visual-c++ malloc stdvector

为什么会崩溃?我确实发现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 个答案:

答案 0 :(得分:7)

array[0] = element;行上,您正在调用operator= array[0]。由于array[0]未初始化,因此这是未定义的行为。在未调用构造函数的对象上调用任何方法或运算符(包括operator=)是未定义的行为。

要解决您的问题,您需要使用placement new来调用array[0]的构造函数,或者只使用new代替malloc。除非你有充分的理由使用malloc,否则后者更可取(甚至更好:使用矢量)。

答案 1 :(得分:4)

这不是你如何初始化元素的原因。 (隐式创建的)赋值运算符(调用向量的赋值运算符)是在一个不存在的对象上调用的,这显然是坏消息。

您必须使用展示位置代替:

new (array) MyStruct;

对于数组:

new (array) MyStruct[100];

答案 2 :(得分:1)

当您分配到MyStruct

array[0] = element;

首先尝试销毁结构的旧成员 - 但没有任何结构,因为它们从未构建过。吊杆!

获得一百MyStruct秒的最简单方法是使用另一个向量

vector<MyStruct>  v(100);

无需使用malloc

答案 3 :(得分:1)

MyStruct *array = (MyStruct *)malloc(100*sizeof(MyStruct));

这是你出错的地方。

array不是指向一个或多个MyStruct对象的指针,无论您给出的是什么类型。 malloc的返回值为void*。 C ++的规则不允许您隐式地从void*转换为其他类型,这就是为什么你必须将(MyStruct*)放在那里。单独进行明确投射的需要应该告诉你,你正在做一些阴暗的事情。

C ++的规则规定,如果您明确地将void*转换为某些Type*(某些特殊类型之外),那么{em>仅合法{} 1}}你正在进行强制转换最初是void*,它本身就被归为Type*。这不是原因;此void*来自void*,而且从来不是malloc。你对编译器撒谎,因此引发了未定义的行为。因此崩溃了。

如果你想要定义的行为,那么你需要实际使用C ++,而不是你正在发明的“我不能相信它不是C ++”语言。例如:

MyStruct*

请注意这里完全没有施法操作。

当然,删除这个数组很痛苦:

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

请注意,您必须按照构造它们的相反顺序向后销毁它们。


  

想知道这些东西在内部是​​如何运作的。 new []做了一些额外的内存告诉我的CPU malloc没有的东西吗?以及为什么/什么/如何在没有新[]的情况下模仿它?或新的位置?我如何用CPU的原始二进制代码编写它?它甚至可能吗?魔法新[]在这里究竟是什么?

所有这些都取决于实现。就语言而言,究竟发生了什么,是明确定义的。除其他外,Placement new将调用该对象的构造函数。数组放置new将按从头到尾的顺序调用数组中所有对象的构造函数。如果其中一个抛出,那么它将在任何先前构造的对象上调用析构函数,然后发出异常。

正在做什么是依赖于实现的,而不仅仅是如何实现继承等等。显然,编译器会为它发出一些代码,但同样,确实发出的是依赖于实现的代码。

只要您实际编写C ++,就不可能“在CPU的原始二进制代码中编写它”。要实现placement new,您必须能够调用该类的构造函数。而且......好吧,这就是添加的新内容。你甚至不允许获得构造函数的成员指针(即使你可以,成员指针的内容依赖于实现,并且它们并不总是指向某个汇编函数的裸指针)。因此,如果没有特定于平台的陪审团操纵,甚至无法识别构造函数代码。

通过查看生成的程序集以调用placement new,您可以了解如何为特定系统执行此操作。但是每个编译器都会有所不同。