使用new运算符进行聚合初始化的跨编译器异常行为咨询
这个问题我刚碰到的时候也挠过半天头,其实是C++标准里动态数组初始化的细节规则,加上不同编译器的实现差异搞出来的状况,咱们一步步拆解看:
先把你提到的三个场景列出来,方便对照:
正常工作的场景1:常量大小数组用字符串字面量初始化
char* test1 = new char[20]{"abc"}; cout << test1; // 输出: abc
这里数组大小20是编译期就能确定的常量表达式,按照C++标准,用字符串字面量初始化这种动态数组时,会把字符串的每个字符依次拷贝到数组前几个位置,剩下的所有元素都会被值初始化(也就是设为'\0')。cout输出到第一个'\0'就停止,结果自然是abc。
正常工作的场景2:运行时大小数组用字符列表初始化
int size = 20; char* test1 = new char[size]{'a','b','c'}; cout << test1; // 输出: abc
这里用单个字符的列表初始化,不管数组大小是不是编译期确定的,C++11及以后的标准都要求:列表里的字符依次赋值给数组前N个元素,剩下的元素如果是POD类型(char属于这类),会被自动值初始化为'\0'。数组第四个元素就是'\0',cout输出到这里就停,结果符合预期。
出问题的场景:运行时大小数组用字符串字面量初始化
int size = 20; char* test1 = new char[size]{"abc"}; cout << test1; // GCC、MSVC输出随机值,Clang输出abc
核心问题就在这:当动态数组的大小是运行时才能确定的(不是常量表达式),用字符串字面量初始化的情况,不同编译器的处理完全不一样。
说白了,早期C++标准里,这种初始化方式是被限制的——只有数组大小是编译期常量表达式时,字符串字面量才能被用来正确初始化动态数组;如果是运行时大小的数组,标准并没有明确要求编译器必须处理这种情况,属于实现定义的行为甚至未定义行为。
GCC和MSVC的实现严格遵循了标准里的限制,它们不会把字符串字面量的字符拷贝到数组里,数组所有元素都是未初始化的垃圾值,cout输出时找不到'\0',就会一直输出内存里的随机内容,直到碰到某个天然存在的'\0'为止。
而Clang则做了个兼容扩展,它不管数组大小是不是编译期确定,都按常量大小数组的方式处理字符串字面量的初始化,把字符拷贝进去,剩余元素设为'\0',所以输出正常。
内容来源于stack exchange

