为什么gcc允许char数组初始化,字符串文字比数组大?

int main() { char a[7] = "Network"; return 0; } 

C中的字符串文字在内部以nul字符终止。 因此,上面的代码应该给出一个编译错误,因为字符串文字Network的实际长度是8,它不能适合char[7]数组。

但是,Ubuntu上的gcc (甚至是-Wall )编译此代码时没有任何错误或警告。 为什么gcc允许这样做而不将其标记为编译错误?

当char数组大小小于字符串文字时,gcc仅发出警告(仍然没有错误!)。 例如,它警告:

 char a[6] = "Network"; 

[相关] Visual C ++ 2012char a[7]提供了编译错误:

 1>d:\main.cpp(3): error C2117: 'a' : array bounds overflow 1> d:\main.cpp(3) : see declaration of 'a' 

使用大于它的字符串文字初始化char数组在C中很好,但在C ++中是错误的。 这解释了gcc和VC ++之间的行为差​​异。

如果使用VC ++编译相同的C文件,则不会出错。 如果您使用g ++将其编译为C ++文件,则会出错。

C标准说:

字符类型数组可以由字符串文字或UTF-8字符串文字初始化,可选地用大括号括起来。 字符串文字的连续字节(如果有空间或数组大小未知,则包括终止空字符)初始化数组的元素。

[…]

例8

声明

 char s[] = "abc", t[3] = "abc"; 

定义”plain”char数组对象st其元素用字符串文字初始化。 该声明与之相同

 char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' }; 

( C11标准草案第6.7.9节,最终标准中的实际措辞可能有所不同。)

这意味着如果arrays没有空间,则删除终止字符是完全正确的。 这可能是意料之外的,但它正是语言应该如何工作,以及(至少对我而言)众所周知的特征。

相反,C ++标准说:

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

例:

  char cv[4] = "asdf"; // error 

由于隐含的尾随’\ 0’没有空格,因此格式错误。

(C ++ 2011草案n3242的 8.5.2。)

声明字符串文字的优先方式通常是:

  char a[] = "Network"; printf("size of a: %d\n", sizeof a); // The compiler 'knows' the size of a. // this prints '8' 

让编译器搞清楚。 手动指定数组大小并使其与字符串文字的实际长度保持同步是很麻烦的…

所以我猜GCC并没有真正打扰任何事情而不是警告。

在C和Unix的早期,内存和磁盘很小,因此不在字符串末尾存储NUL字节实际上是一种使用的技术。 如果字符串变量长度为7个字符,则可以在其中存储7个字符的字符串,并且由于7是最大长度,因此即使没有终结符,您也知道字符串在那里结束。 这就是为什么strncpy以它的方式工作的原因。

虽然放松的答案解释了为什么gcc没有对此发出警告,但它没有说明你能做些什么。

gcc-Wc++-compat警告选项将使用以下消息检测此特定问题:

 foo.c: In function 'main': foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat] 

这是导致gcc警告这个问题的唯一选择。 您可以编写一个简短的脚本来快速地从gcc的手册页中查看警告选项,尝试编译每个警告选项,看看它是否有抱怨。

 $ time for F in $(man gcc | grep -o -- '-W[^= ]*') do if gcc -c "${F}" foo.c |& grep :3 >& /dev/null; then echo "${F}"; gcc -c "${F}" foo.c fi done man gcc | grep -o -- '-W[^= ]*') man gcc | grep -o -- '-W[^= ]*' -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wc++-compat foo.c: In function 'main': foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused-variable foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wtraditional foo.c: In function 'main': foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused-variable foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused-variable foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wunused foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wtraditional foo.c: In function 'main': foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] -Wtraditional foo.c: In function 'main': foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] -Wc++-compat foo.c: In function 'main': foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wtraditional foo.c: In function 'main': foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wall foo.c: In function 'main': foo.c:3:10: warning: unused variable 'a' [-Wunused-variable] -Wtraditional foo.c: In function 'main': foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional] real 0m26.399s user 0m5.128s sys 0m15.329s 

一般来说,像splint这样的棉绒工具会警告你各种各样的潜在问题。 在这种情况下,它会说:

 foo.c:3:17: String literal with 8 characters is assigned to char [7] (no room for null terminator): "Network" A string literal is assigned to a char array that is not big enough to hold the null terminator. (Use -stringliteralnoroom to inhibit warning) foo.c:3:10: Variable a declared but not used