简体   繁体   English

堆上的结构数组未正确初始化

[英]Array of structs on heap not properly initialized

I thought I knew how to deal with memory management in c++ but this confused me: 我以为我知道如何用c ++处理内存管理,但这使我感到困惑:

Consider the following code: 考虑以下代码:

struct A {
    int i;
};

int main(int argc, char* argv[]) {
    A a{ 5 }; //Constructs an A object on the stack
    A* b = new A{ 7 }; //Constructs an A object on the heap and stores a pointer to it in b
    A* c = new A[] { //Construct an array of A objects on the heap and stores a pointer to it in c
        { 3 },
        { 4 },
        { 5 },
        { 6 }
    };
    std::cout << "a: " << a.i << "\n"; //Prints 'a: 5'
    std::cout << "b: " << b->i << "\n"; //Prints 'b: 7'
    std::cout << "c: " << c[0].i << "; " << c[1].i << "; " << c[2].i << "; " << c[3].i << "\n"; 
    //Prints 'c: -33686019; -1414812757; -1414812757; -1414812757'

    delete b;
    delete[] c;
    return 0;
}

I don't understand why the last print-out of c prints those weird numbers. 我不明白为什么c的最后打印输出会打印那些怪异的数字。 If I add a constructor to A like so: 如果我像这样向A添加构造函数:

struct A {
    A(int i) : i{i} {}
    int i;
};

Then the output of the last print-out becomes: 然后,最后打印输出的输出变为:

'c: 3; 4; 5; 6'

as it should be. 应该是。 But now delete[] c; 但是现在delete[] c; will give me a runtime error (not an exception it seems) that says MyGame.exe has triggered a breakpoint. 会给我一个运行时错误(看来不是异常),它表示MyGame.exe has triggered a breakpoint. (I'm working in VS2013). (我正在VS2013中工作)。

Furthermore, if I change the line A* c = new A[] { to A* c = new A[4] { the error disappears and everything works as expected. 此外,如果我将A* c = new A[] {更改为A* c = new A[4] {该错误将消失并且一切都会按预期进行。

So my questions are: Why the weird numbers? 所以我的问题是:为什么数字怪异? Won't the A objects in the array get properly constructed somehow if I don't define a constructor? 如果不定义构造函数,数组中的A对象是否会以某种方式正确构造? And why do I need to specify the array size explicitly even though it will compile and link just fine without? 为何即使不编译也可以链接时,为什么仍需要显式指定数组大小? Initializing arrays on the stack this way does not give me a runtime error (I tested it to be sure). 用这种方法在堆栈上初始化数组不会给我一个运行时错误(为了确定我已经对其进行了测试)。

This is an error: 这是一个错误:

A* c = new A[] { {3}, {4}, {5}, {6} };

You must put the dimension inside the [] . 您必须将尺寸放在[] With new the array dimension cannot be deduced from the initializer list. 使用new ,无法从初始化列表中推导出数组维。

Putting 4 in here makes your code work correctly for me. 在此处输入4可以使您的代码对我来说正常工作。

Your compiler apparently has an "extension" that treats new A[] as new A[1] . 您的编译器显然具有将new A[]视为new A[1]的“扩展名”。

If you compile in standard mode (with gcc or clang, -std=c++14 -pedantic ), which is always a good idea, the compiler will tell you about things like this. 如果您以标准模式(使用gcc或clang -std=c++14 -pedantic )进行编译(这总是一个好主意),则编译器会告诉您类似情况。 Treat warnings as errors unless you are really sure they are not errors :) 除非您真的确定警告不是错误,否则将警告视为错误:)

Why the weird numbers? 为什么数字怪异?

Because no memory was allocated to back them. 因为没有分配内存来支持它们。 The pointer is pointing at Crom knows what. 指针指向Crom知道什么。 That structure should not compile. 该结构不应该编译。

Won't the A objects in the array get properly constructed somehow if I don't define a constructor? 如果不定义构造函数,数组中的A对象是否会以某种方式正确构造?

Without a constructor all of the members will be initialized to their defaults. 没有构造函数,所有成员将被初始化为其默认值。 int 's and most Plain Old Datatypes have no defined default value. int和大多数Plain Old Datatypes没有定义默认值。 In a typical implementation they get whatever value happens to already be in their allocated memory block. 在典型的实现中,它们将获得恰好在其分配的内存块中的任何值。 If a member object is of a type that doesn't default constructor and is unable to make one, you get a compiler error. 如果成员对象的类型不是默认构造函数且无法创建,则您会收到编译器错误。

And why do I need to specify the array size explicitly even though it will compile and link just fine without? 为何即使不编译也可以链接时,为什么仍需要显式指定数组大小?

It shouldn't compile, mismatch between the size of the array (unspecified and an error unto itself) and the number of elements in the initializer list, so the compiler has a bug. 它不应该编译,数组大小(未指定和自身错误)与初始化列表中的元素数量不匹配,因此编译器存在错误。 Linker is not involved at this point. 此时不涉及链接器。

Initializing arrays on the stack this way does not give me a runtime error (I tested it to be sure). 用这种方法在堆栈上初始化数组不会给我一个运行时错误(为了确定我已经对其进行了测试)。

In the static version the compiler can count the number of elements in initialization list. 在静态版本中,编译器可以计算初始化列表中的元素数量。 Why the dynamic version with new can't, gotta say I have no good answer. 为什么不能使用新的动态版本,我必须说我没有好的答案。 You'd think it would be a simple bit of counting that initializer list, so there's something deeper preventing it. 您可能认为只需要对初始化列表进行计数就可以了,因此还有一些更深的层次可以防止它的出现。 The folk who debated and then approved the standard either never considered allocating a dynamic array that way or couldn't find a good way to make it work in all cases. 争论并随后批准该标准的人们从未考虑过以这种方式分配动态数组,或者找不到一种在所有情况下都能使其正常工作的好方法。 Same reason variable length arrays still aren't in the standard. 同样的原因,可变长度数组仍然不在标准中。

"And why do I need to specify the array size explicitly even though it will compile and link just fine without? It shouldn't compile, ...." To be clear: If I add the constructor to A and run it, it runs just fine up until the delete[] statement. “并且为什么我需要显式指定数组大小,即使它可以编译和链接也没问题呢?它不应该编译,...。”要明确:如果我将构造函数添加到A并运行它,它将运行直到删除[]语句为止。 Only then it crashes but cout << c[0] works as 'expected' 只有这样,它才会崩溃,但是cout << c [0]可以按“预期”的方式工作

This is because you are unlucky. 这是因为您很不幸。 That constructor is writing into memory that your program owns, but didn't allocate to c . 该构造函数正在写入程序拥有的内存,但没有分配给c Printing those values works, but whatever was supposed to be in memory at that point has been overwritten. 打印这些值是可行的,但是那时应该存储在内存中的内容已被覆盖。 This will probably cause your program to crash sooner or later. 这可能会导致您的程序迟早崩溃。 This time it's later. 这次是晚了。

My suspicions, and this is guesswork based on specific because you've ventured far into the realms of the undefined, are the crash on delete[] is because 我的怀疑是基于特定的猜测,因为您已经冒险进入undefined领域,因为delete[]崩溃是因为

A* c = new A[]

Allocated A[1] and assigned it to c rather than failing to compile. 分配A[1]并将其分配给c而不是编译失败。 c has one A to work with. c有一个A可使用。 The initializer list tries to stuff in 4 and writes 3 into c[0] and the 4,5, and 6 over the heap control information that delete needs to put the data back. 初始化程序列表尝试填充4,然后将3写入c[0]以及将删除需要放回数据的堆控制信息上的4,5和6。 All looks good until delete tries to use that overwritten information. 在删除尝试使用该覆盖的信息之前,一切看起来都很不错。

Oh and this:"Without a constructor all of the members will be initialized to their defaults. int's and most Plain Old Datatypes have no defined default value.". 哦,这:“没有构造函数,所有成员将被初始化为其默认值。int和大多数Plain Old Datatypes没有定义的默认值。” For structs a user defined ctor seems optional because you can initialize a struct by providing arguments corresponding to its data fields. 对于结构,用户定义的ctor似乎是可选的,因为您可以通过提供对应于其数据字段的参数来初始化结构。

A struct has a much more permissive attitude toward data encapsulation than a class and defaults to public access where a class defaults to private . 与类相比,结构对数据封装的态度要宽松得多,并且默认为public访问,而类默认为private I've never tried it, but I'm betting that you can use the same struct trick to init all the public members of a class. 我从未尝试过,但是我敢打赌,您可以使用相同的结构技巧来初始化类的所有公共成员。

OK. 好。 Just tried it. 刚刚尝试过。 Works in GCC 4.8.1. 在GCC 4.8.1中工作。 Not going to make that claim in general without looking it up in the standard. 一般情况下,如果不在标准中查找它,就不会提出该主张。 Got to get a copy of it. 得到了它的副本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM