在gcc中将2D数组初始化为0时,值不正确

dev*_*nut 12 c++ gcc variable-length-array

#include <iostream>
using namespace std;

int main() {

    int rows = 10;
    int cols = 9;
    int opt[rows][cols] = {0};

         for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                std::cout << opt[i][j] << " ";
            }
             std::cout << "\n";
         }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

0 32767 1887606704 10943 232234400 32767 1874154647 10943 -1 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
Run Code Online (Sandbox Code Playgroud)

我在https://www.codechef.com/ide中使用gcc 6.3

我期待第一行全为零.不应该是这样吗?

编辑:我测试了const变量的行和列,然后它初始化为全零.我觉得这应该抛出一个编译错误,而不是表现出这种不正确(并且有潜在危险)的行为.

Sha*_*our 12

如果我们看一下gcc 4.9发行说明,看起来他们添加了对初始化VLA的支持,期望在未来的C++版本中支持VLA:

G ++支持C++ 1y可变长度数组.G ++长期以来一直支持GNU/C99风格的VLA,但现在还通过引用支持初始化器和lambda捕获.在C++ 1y模式中,G ++会抱怨草案标准不允许的VLA使用,例如形成指向VLA类型的指针或将sizeof应用于VLA变量.请注意,现在看来,VLA不会成为C++ 14的一部分,但它将成为单独文档的一部分,然后可能是C++ 17.

我们可以看到它在4.9抱怨之前我们无法初始化VLA

error: variable-sized object 'opt' may not be initialized  
     int opt[rows][cols] = {0};  
                             ^
Run Code Online (Sandbox Code Playgroud)

但是在4.9.1之后它停止抱怨并且它没有我们在更新版本中看到的相同的错误.

所以它看起来像一个回归.

请注意,clang拒绝允许初始化VLA(它们支持作为扩展)参见实例.这是有道理的,因为C99不允许初始化VLA:

要初始化的实体的类型应该是未知大小的数组或不是可变长度数组类型的对象类型.

gcc Bug 69517

gcc bug报告:带有多余初始化元素的VLA上的SEGV有一条注释,提供了有关此功能的一些背景知识:

(在评论#16中回复Jakub Jelinek)

这里的错误是在G ++中接受VLA初始化程序,其中包含的元素多于VLA中的空间,然后在运行时使用额外元素将堆栈废弃.它是关于GCC 4.9.3的回归,它实现了n3639中规定的C++ VLA(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3639.html).这在GCC 4.9更改(https://gcc.gnu.org/gcc-4.9/changes.html)中有记录,它使用以下示例突出显示该功能:

  void f(int n) {
    int a[n] = { 1, 2, 3 }; // throws std::bad_array_length if n < 3
    ...
Run Code Online (Sandbox Code Playgroud)

随后从C++中删除了VLA,并且还从G ++中部分地(但不是完全地)移除了VLA,这导致在使用G ++ 4.9开发和测试的C++程序在移植到更高版本时中断.

使用注释#9中引用的补丁更安全地使用C++ VLA.它补丁必须从GCC 6.0中恢复,因为它在Java中引起了问题.Java已被删除,我计划/希望重新提交GCC 8的补丁.(我想为GCC 7做这个但是没有达到它.)


zne*_*eak 5

这似乎是一个GCC错误,并且所谓的行为很可能是不应该编译的.C99支持可变长度数组,但拒绝初始化它们:C初始化程序需要在编译时知道它们的类型,但是在编译时不能完成可变长度数组的类型.

在GCC中,C++将可变长度数组作为C99支持的扩展.因此,C++中不能建立控制C++中可变长度数组初始化的行为.即使在C++中,Clang也拒绝初始化可变长度数组.

请注意,即使= {0}在技​​术上有点危险(如果它完全有效):如果rowscols0,你将会溢出.Memset可能是您的最佳选择.

  • @HolyBlackCat,你如何推断​​`= {}`应该有效?`{}`需要与它初始化的东西具有相同的类型,并且该类型是未知的. (2认同)