19
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

4 回答 4

31

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

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

C标准说:

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

[...]

例 8

宣言

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

定义 ''plain'' 字符数组对象st其元素用字符串字面量初始化。此声明与

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

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

这意味着如果数组没有空间放置终止字符,则删除它是完全正确的。这可能是出乎意料的,但这正是该语言应该如何工作的,并且是(至少对我而言)众所周知的功能。

相反,C++ 标准说:

初始化器的个数不得多于数组元素的个数。

例子:

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

格式不正确,因为没有用于隐含尾随 '\0' 的空间。

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

于 2012-11-21T10:12:42.210 回答
3

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

于 2012-11-21T10:20:59.957 回答
3

虽然unwind 的回答解释了为什么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警告此问题的选项。您可以编写一个简短的脚本来快速 grep 的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

一般来说,类似lint的工具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
于 2012-11-22T16:58:11.937 回答
2

声明字符串文字的首选方式通常是:

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

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

所以我猜GCC除了警告之外并没有真正打扰任何事情。

于 2012-11-21T10:15:53.747 回答