为什么在初始化元素多于数组大小时的数组容器时不会出现错误?

Why I do not get errors when I initialize an array container with more elements than the size of the array?

我注意到当我像这样初始化数组时:

std::array<int,3> myarray;

myarray[0] = 9;
myarray[1] = 8;
myarray[2] = 7;
myarray[3] = 6;
myarray[4] = 5;

我没有收到错误,即使我的数组元素比 <int,3>

中定义的多

我正在 Mac 使用 g++,它是 C++11。

所以我想我的问题是:为什么没有错误?这是正确的行为吗?还是我对我的编译器期望太多?

还有这个初始化方法叫什么?有人称它为"buggy construction"。我认为他们只是在开玩笑。

P.S。我理解这个方法是.

编辑: 有些人建议我的问题是 duplicate。但根据 Vlad 的最佳答案,似乎并非如此。根据 Vlad 的说法,我的问题是关于运算符重载而不是 C 样式数组范围。我不得不相信他的专业知识,他说的很有道理。

undefined behavior。编译器不必警告您未定义的行为。

如果你真的想知道你什么时候超出了数组的边界,那么使用 at() 会抛出异常。

一般情况下,编译器不会检查数组下标运算符中使用的索引。

至于 class std::array 然后下标运算符被重载,看起来像

T & operator []( size_t i );

所以这条记录

myarray[4] = 5;

等同于

myarray.operator []( 4 ) = 5;

语法正确。所以编译器没有理由发出错误。

... why is that, that there are no errors? Is it correct behaviour?

好吧,代码调用了未定义的行为。正如您所观察到的那样,它不会做任何明显的事情,也可能会引发 运行时间错误。

在这些情况下,您通常可以 运行 解决三种类型的错误;

  • 编译时数组初始化错误
  • 运行超时错误
  • 未定义的行为

编译时数组初始化错误

当用超过数组大小的元素初始化数组时,编译器可以而且确实会发出错误。它有足够的信息来诊断这些,因此它确实如此。

std::array<int,3> my_array = {1, 2, 3, 4, 5}; // compiler error

运行超时错误

索引时进入std::array (and other standard library containers), there are generally two forms; using the index operator [] or using a member method at()。索引运算符通常是为了性能而实现的,因此不会进行任何边界检查,另一方面,at() 会进行边界检查,如果索引元素大于数组大小,则会抛出异常。

编译器通常没有足够的信息来将这些情况诊断为错误,因此它不会 - 但您可能会收到警告。存在其他工具以在构建期间捕获这些错误(在给定的示例中可能是这样)。

未定义的行为

这基本上是边界检查的另一种情况。如果索引元素超出错误范围,则行为未定义。它可能会导致分段错误(或访问冲突),也可能不会引发任何 运行时间错误或异常。

如果您使用 Microsoft 的 GSL 跨度实现它,它将失败 fast,正如 Neil Macintosh 所承诺的那样。因此,在任何一种情况下都可能不可取,但使用这种方法,程序将终止并指示一个值得调查的问题。

array<int, 3> a;
a[0] = 0;
a[1] = 1;
a[2] = 2;
a[3] = 3;
a[4] = 4;

for (auto& i : a) {
    cout << i << endl;
}

gsl::span<int, 3> s = a;
s[0] = 10;
s[1] = 11;
s[2] = 12;
//    s[3] = 13;

for (auto& i : s) {
    cout << i << endl;
}

产出

0
1
2
10
11
12

取消注释 s[3] = 13 编译但失败。

0
1
2
libc++abi.dylib: terminating
zsh: abort      ./gsl_span