为什么编译器没有检测到字符串常量初始化中的越界?
我在一本书中读到了这个问题及其答案。 但我不明白这本书的理由。
下面的代码会编译吗?
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]当你试图运行它时,这可能是段错误的,我更可能认为数组只是被初始化为“快”。