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

时间:2012-11-21 10:07:58

标签: c++ c compiler-errors string-literals arrays

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 ++ 2012 char a[7]提供了编译错误:

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

4 个答案:

答案 0 :(得分:25)

使用大于它的字符串文字初始化一个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 draft standard的第6.7.9节,最终标准中的实际措辞可能有所不同。)

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

相反,C ++标准说:

  

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

     

示例:

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

格式错误,因为隐含的尾随'\ 0'没有空格。

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

答案 1 :(得分:3)

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

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

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

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

答案 2 :(得分:3)

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

答案 3 :(得分:2)

虽然unwind's answer解释了为什么gcc没有对此发出警告,但它没有说明您可以采取的措施。

gcc的{​​{1}}警告选项会通过以下消息检测到此特定问题:

-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

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

$ 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