繁体   English   中英

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

[英]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对象是否会以某种方式正确构造? 为何即使不编译也可以链接时,为什么仍需要显式指定数组大小? 用这种方法在堆栈上初始化数组不会给我一个运行时错误(为了确定我已经对其进行了测试)。

这是一个错误:

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

您必须将尺寸放在[] 使用new ,无法从初始化列表中推导出数组维。

在此处输入4可以使您的代码对我来说正常工作。

您的编译器显然具有将new A[]视为new A[1]的“扩展名”。

如果您以标准模式(使用gcc或clang -std=c++14 -pedantic )进行编译(这总是一个好主意),则编译器会告诉您类似情况。 除非您真的确定警告不是错误,否则将警告视为错误:)

为什么数字怪异?

因为没有分配内存来支持它们。 指针指向Crom知道什么。 该结构不应该编译。

如果不定义构造函数,数组中的A对象是否会以某种方式正确构造?

没有构造函数,所有成员将被初始化为其默认值。 int和大多数Plain Old Datatypes没有定义默认值。 在典型的实现中,它们将获得恰好在其分配的内存块中的任何值。 如果成员对象的类型不是默认构造函数且无法创建,则您会收到编译器错误。

为何即使不编译也可以链接时,为什么仍需要显式指定数组大小?

它不应该编译,数组大小(未指定和自身错误)与初始化列表中的元素数量不匹配,因此编译器存在错误。 此时不涉及链接器。

用这种方法在堆栈上初始化数组不会给我一个运行时错误(为了确定我已经对其进行了测试)。

在静态版本中,编译器可以计算初始化列表中的元素数量。 为什么不能使用新的动态版本,我必须说我没有好的答案。 您可能认为只需要对初始化列表进行计数就可以了,因此还有一些更深的层次可以防止它的出现。 争论并随后批准该标准的人们从未考虑过以这种方式分配动态数组,或者找不到一种在所有情况下都能使其正常工作的好方法。 同样的原因,可变长度数组仍然不在标准中。

“并且为什么我需要显式指定数组大小,即使它可以编译和链接也没问题呢?它不应该编译,...。”要明确:如果我将构造函数添加到A并运行它,它将运行直到删除[]语句为止。 只有这样,它才会崩溃,但是cout << c [0]可以按“预期”的方式工作

这是因为您很不幸。 该构造函数正在写入程序拥有的内存,但没有分配给c 打印这些值是可行的,但是那时应该存储在内存中的内容已被覆盖。 这可能会导致您的程序迟早崩溃。 这次是晚了。

我的怀疑是基于特定的猜测,因为您已经冒险进入undefined领域,因为delete[]崩溃是因为

A* c = new A[]

分配A[1]并将其分配给c而不是编译失败。 c有一个A可使用。 初始化程序列表尝试填充4,然后将3写入c[0]以及将删除需要放回数据的堆控制信息上的4,5和6。 在删除尝试使用该覆盖的信息之前,一切看起来都很不错。

哦,这:“没有构造函数,所有成员将被初始化为其默认值。int和大多数Plain Old Datatypes没有定义的默认值。” 对于结构,用户定义的ctor似乎是可选的,因为您可以通过提供对应于其数据字段的参数来初始化结构。

与类相比,结构对数据封装的态度要宽松得多,并且默认为public访问,而类默认为private 我从未尝试过,但是我敢打赌,您可以使用相同的结构技巧来初始化类的所有公共成员。

好。 刚刚尝试过。 在GCC 4.8.1中工作。 一般情况下,如果不在标准中查找它,就不会提出该主张。 得到了它的副本。

暂无
暂无

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

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