堆上的结构数组未正确初始化
Array of structs on heap not properly initialized
我以为我知道如何在 C++ 中处理内存管理,但这让我很困惑:
考虑以下代码:
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;
}
我不明白为什么 c
的最后打印输出会打印出那些奇怪的数字。如果我像这样向 A 添加构造函数:
struct A {
A(int i) : i{i} {}
int i;
};
那么最后打印出来的输出变成:
'c: 3; 4; 5; 6'
理应如此。但是现在 delete[] c;
会给我一个运行时错误(看起来不是异常),上面写着 MyGame.exe has triggered a breakpoint.
(我在 VS2013 中工作)。
此外,如果我将行 A* c = new A[] {
更改为 A* c = new A[4] {
,错误就会消失,一切都会按预期进行。
所以我的问题是:
为什么会有奇怪的数字?如果我不定义构造函数,数组中的 A 对象是否会以某种方式正确构造?
为什么我需要显式指定数组大小,即使它会编译并且 link 没有编译也很好?以这种方式初始化堆栈上的数组不会给我运行时错误(我测试过它是肯定的)。
这是一个错误:
A* c = new A[] { {3}, {4}, {5}, {6} };
您必须将维度放在 []
内。使用 new
时,无法从初始化列表中推导出数组维数。
在这里输入 4
可以使您的代码对我来说正常工作。
您的编译器显然有一个 "extension" 将 new A[]
视为 new A[1]
。
如果您在标准模式下编译(使用 gcc 或 clang,-std=c++14 -pedantic
),这总是一个好主意,编译器会告诉您类似这样的事情。将警告视为错误,除非您真的确定它们不是错误:)
Why the weird numbers?
因为没有分配内存来支持它们。指针指向 Crom 知道什么。该结构不应编译。
Won't the A objects in the array get properly constructed somehow if I don't define a constructor?
如果没有构造函数,所有成员都将被初始化为其默认值。 int
和大多数普通旧数据类型都没有定义默认值。在一个典型的实现中,他们得到任何恰好已经在他们分配的内存块中的值。如果成员对象的类型没有默认构造函数并且无法创建默认构造函数,则会出现编译器错误。
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).
在静态版本中,编译器可以计算初始化列表中元素的数量。为什么带有 new 的动态版本不能,得说我没有好的答案。你可能认为计算初始化列表很简单,所以有更深层次的东西阻止它。辩论然后批准该标准的人要么从未考虑过以这种方式分配动态数组,要么找不到使其在所有情况下都能正常工作的好方法。同样的原因可变长度数组仍然不在标准中。
"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. Only then it crashes but cout << c[0] works as 'expected'
这是因为你运气不好。该构造函数正在写入您的程序拥有的内存,但没有分配给 c
。打印这些值是可行的,但是此时应该在内存中的任何内容都已被覆盖。这可能迟早会导致您的程序崩溃。这次是晚了。
我的怀疑,这是基于特定的猜测,因为你已经冒险进入未定义的领域,delete[]
上的崩溃是因为
A* c = new A[]
已分配 A[1]
并将其分配给 c
而不是编译失败。 c
有一个 A 可以使用。 initializer list 尝试填充 4 并将 3 写入 c[0]
和 4,5, 6 over the heap control information that delete 需要将数据放回。在删除尝试使用被覆盖的信息之前,一切看起来都很好。
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.". For structs a user defined ctor seems optional because you can initialize a struct by providing arguments corresponding to its data fields.
结构对数据封装的态度比 class 更宽松,默认为 public
访问,而 class 默认为 private
。我从未尝试过,但我敢打赌,您可以使用相同的结构技巧来初始化 class 的所有 public 成员。
好的。刚试过了。适用于 GCC 4.8.1。如果不在标准中查找,一般不会提出该声明。必须得到它的副本。
我以为我知道如何在 C++ 中处理内存管理,但这让我很困惑:
考虑以下代码:
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;
}
我不明白为什么 c
的最后打印输出会打印出那些奇怪的数字。如果我像这样向 A 添加构造函数:
struct A {
A(int i) : i{i} {}
int i;
};
那么最后打印出来的输出变成:
'c: 3; 4; 5; 6'
理应如此。但是现在 delete[] c;
会给我一个运行时错误(看起来不是异常),上面写着 MyGame.exe has triggered a breakpoint.
(我在 VS2013 中工作)。
此外,如果我将行 A* c = new A[] {
更改为 A* c = new A[4] {
,错误就会消失,一切都会按预期进行。
所以我的问题是: 为什么会有奇怪的数字?如果我不定义构造函数,数组中的 A 对象是否会以某种方式正确构造? 为什么我需要显式指定数组大小,即使它会编译并且 link 没有编译也很好?以这种方式初始化堆栈上的数组不会给我运行时错误(我测试过它是肯定的)。
这是一个错误:
A* c = new A[] { {3}, {4}, {5}, {6} };
您必须将维度放在 []
内。使用 new
时,无法从初始化列表中推导出数组维数。
在这里输入 4
可以使您的代码对我来说正常工作。
您的编译器显然有一个 "extension" 将 new A[]
视为 new A[1]
。
如果您在标准模式下编译(使用 gcc 或 clang,-std=c++14 -pedantic
),这总是一个好主意,编译器会告诉您类似这样的事情。将警告视为错误,除非您真的确定它们不是错误:)
Why the weird numbers?
因为没有分配内存来支持它们。指针指向 Crom 知道什么。该结构不应编译。
Won't the A objects in the array get properly constructed somehow if I don't define a constructor?
如果没有构造函数,所有成员都将被初始化为其默认值。 int
和大多数普通旧数据类型都没有定义默认值。在一个典型的实现中,他们得到任何恰好已经在他们分配的内存块中的值。如果成员对象的类型没有默认构造函数并且无法创建默认构造函数,则会出现编译器错误。
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).
在静态版本中,编译器可以计算初始化列表中元素的数量。为什么带有 new 的动态版本不能,得说我没有好的答案。你可能认为计算初始化列表很简单,所以有更深层次的东西阻止它。辩论然后批准该标准的人要么从未考虑过以这种方式分配动态数组,要么找不到使其在所有情况下都能正常工作的好方法。同样的原因可变长度数组仍然不在标准中。
"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. Only then it crashes but cout << c[0] works as 'expected'
这是因为你运气不好。该构造函数正在写入您的程序拥有的内存,但没有分配给 c
。打印这些值是可行的,但是此时应该在内存中的任何内容都已被覆盖。这可能迟早会导致您的程序崩溃。这次是晚了。
我的怀疑,这是基于特定的猜测,因为你已经冒险进入未定义的领域,delete[]
上的崩溃是因为
A* c = new A[]
已分配 A[1]
并将其分配给 c
而不是编译失败。 c
有一个 A 可以使用。 initializer list 尝试填充 4 并将 3 写入 c[0]
和 4,5, 6 over the heap control information that delete 需要将数据放回。在删除尝试使用被覆盖的信息之前,一切看起来都很好。
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.". For structs a user defined ctor seems optional because you can initialize a struct by providing arguments corresponding to its data fields.
结构对数据封装的态度比 class 更宽松,默认为 public
访问,而 class 默认为 private
。我从未尝试过,但我敢打赌,您可以使用相同的结构技巧来初始化 class 的所有 public 成员。
好的。刚试过了。适用于 GCC 4.8.1。如果不在标准中查找,一般不会提出该声明。必须得到它的副本。