下面的行为是否定义明确?
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int a[10] = {1, 2, 3, 4, 5};
for(const auto &i: a)
cout << i << endl;
return 0;
}
输出:
1
2
3
4
5
0
0
0
0
0
是的,多余的元素被初始化为“零”(整数为 0,浮点数为 0.0,指针指向NULL
)。
更准确地说,C 标准要求它们被初始化,就好像它们具有static
存储持续时间一样:
C99 标准,第 6.7.8.21 段:
如果大括号括起来的列表中的初始值设定项少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,则聚合的其余部分应隐式初始化与具有静态存储持续时间的对象相同。
6.7.8.10:
如果具有自动存储持续时间的对象未显式初始化,则其值是不确定的。如果具有静态存储持续时间的对象未显式初始化,则:
— 如果它具有指针类型,则将其初始化为空指针;
— 如果它具有算术类型,则将其初始化为(正或无符号)零;
— 如果是聚合,则每个成员都根据这些规则(递归地)初始化;
— 如果是联合,则根据这些规则(递归地)初始化第一个命名的成员。
该死的,这是C++。(除了@yuan 没有人意识到这一点,谢谢!)
所以 C++11 中的第 8.5.1.7 段:
对 T 类型的对象进行值初始化意味着:
— 如果 T 是具有用户提供的构造函数(12.1)的(可能是 cv 限定的)类类型(第 9 条),则调用 T 的默认构造函数(如果 T 没有可访问的默认构造函数,则初始化是非良构的);
— 如果 T 是一个(可能是 cv 限定的)非联合类类型,没有用户提供的构造函数,则该对象是零初始化的,如果 T 的隐式声明的默认构造函数是非平凡的,则调用该构造函数。
— 如果 T 是一个数组类型,那么每个元素都是值初始化的;
— 否则,对象被零初始化。
8.5.1.5:
对 T 类型的对象或引用进行零初始化意味着:
— 如果 T 是标量类型(3.9),则将对象设置为值 0(零),作为整数常量表达式,转换为 T;103
— 如果 T 是(可能是 cv 限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都被初始化为零,并且填充被初始化为零位;
— 如果 T 是(可能是 cv 限定的)联合类型,则对象的第一个非静态命名数据成员被零初始化,填充被初始化为零位;
— 如果 T 是数组类型,则每个元素都初始化为零;
— 如果 T 是引用类型,则不执行初始化。