为什么编译器没有检测到字符串常量初始化中的越界?

我在一本书中读到了这个问题及其答案。 但我不明白这本书的理由。

下面的代码会编译吗?

int main() { char str[5] = "fast enough"; return 0; } 

答案是:

是的。如果超出数组的边界,编译器永远不会检测到错误。

我无法得到它。

任何人都可以解释一下吗?

在C ++标准中,8.5.2 / 2字符数组表示:

没有比数组元素更多的初始化器。

在C99标准中,6.7.8 / 2初始化说:

初始化程序不应尝试为未初始化的实体中包含的对象提供值

C90 6.5.7初始化器说类似。

但请注意,对于C(C90和C99), 如果有空间 ,’\ 0’终止字符将被放入数组中。 如果终结符不适合,则不是错误(C99 6.7.8 / 14:“字符串文字的连续字符(如果有空间或者数组大小未知,则包括终止空字符)初始化元素arrays“)。

另一方面,C ++标准有一个例子,表明如果终止字符没有空间,应该诊断出错误。

在任何一种情况下,这应该被诊断为所有编译器中的错误:

 char str[5] = "fast enough"; 

也许ANSI之前的编译器并不是那么严格,但任何合理的现代编译器都应该对此进行诊断。

你的书必须很旧,因为即使没有-Wall打开,gcc也会发出警告:

 $ gcc cc
 cc:在函数`main'中:
 cc:6:警告:chars数组的初始化字符串太长

如果我们稍微更新程序:

 #include  int main(int argc, char **argv) { char str[5] = "1234567890"; printf("%s\n", str); return 0; } 

我们可以看到gcc似乎将字符串截断为你指定的长度; 我假设 恰好有一个'\0' ,其中str[6]将是,因为否则我们应该看到5之后的垃圾; 但也许gcc隐含地使str成为长度为6的数组并自动将'\0'放在那里 – 我不确定。

 $ gcc cc && ./a.exe
 cc:在函数`main'中:
 cc:6:警告:chars数组的初始化字符串太长
 12345

您引用的问题的答案是错误的。 正确答案是“否则代码将无法编译” ,假设一个正式正确的C编译器(与某些特定编译器的怪癖相反)。

C语言不允许使用过长的字符串文字初始化特定大小的字符数组。 这里语言允许的唯一灵活性是终止\0字符。 如果数组太短而无法容纳终止\0 ,则会以静默方式删除终止\0 。 但是不能删除实际的文字字符串字符。 如果文字太长,则违反约束,编译器必须发出诊断消息。

 char s1[5] = "abc"; /* OK */ char s2[5] = "abcd"; /* OK */ char s3[5] = "abcde"; /* OK, zero at the end is dropped (ERROR in C++) */ char s4[5] = "abcdef"; /* ERROR, initializer is too long (ERROR in C++ as well) */ 

谁写了你的“书”确实知道他们在谈论什么(至少在这个特定的主题上)。 他们在答案中陈述的内容是不正确的。

注意:在C89 / 90,C99和C ++中,提供过长的字符串初始值设定项是非法的。 然而,在这方面,C ++甚至更具限制性。 C ++禁止丢弃终止\0字符,而C允许丢弃它,如上所述。

数组绑定检查在运行时发生,而不是编译时。 编译器无法对上述代码进行静态分析,以防止出现错误。

更新:显然上述陈述适用于某些编译器而非其他编译器。 如果你的书说它会编译,那么它必须指的是不进行检查的编译器。

因为“足够快”只是一个指向空终止字符串的指针。 编译器要弄清楚是否对char *或char []的赋值超出了数组的范围,这是太多的工作。

发生的事情是你正在尝试初始化一个字符数组,其中包含的字符多于数组所具有的空间。 以下是它如何分解:

 char str[5]; 

声明一个包含五个字符的字符数组。

 char str[5] = "fast enough"; 

第二部分’=“足够快”;’ 然后尝试使用“足够快”的值初始化该数组。 这不起作用,因为“足够快”比数组长。

然而,它将编译。 C和C ++编译器通常不能为您执行数组边界检查,并且超出数组是分段错误的最常见原因之一。 [编辑]正如Mark Rushakoff所指出的那样,显然有些情况下新的确实会发出警告。[/ edit]当你试图运行它时,这可能是段错误的,我更可能认为数组只是被初始化为“快”。