【问题标题】:Why does gcc allow char array initialization with string literal larger than array?为什么 gcc 允许字符串字面量大于数组的字符数组初始化?
【发布时间】:2012-11-09 13:25:13
【问题描述】:
int main()
{
    char a[7] = "Network";
    return 0;
}

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

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

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

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'

【问题讨论】:

  • 也许您正在使用 gcc 将代码编译为 C,在 Visual Studio 中编译为 C++。它们只是表面上相似的语言。
  • @nos:是的,这解释了这种行为!我想这是 C 和 C++ 之间类 C 代码的微小差异之一。
  • 如果我没记错的话,g++会出错

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


【解决方案1】:

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

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

C 标准说:

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

[...]

示例 8

声明

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

定义“普通”字符数组对象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 草案的 8.5.2 n3242.)

【讨论】:

  • 展开:如果标准允许,知道为什么 VC++ 将其标记为错误吗?
  • 展开:如果空字符已经从文字中删除(在上面的初始化中),那么在 strlen 之类的函数中使用这样的字符串是否安全?
  • @Ashwin 不确定 Visual Studio,也许它正在编译为 C++,其规则 可能 有所不同?不,这不安全,因为strlen() 需要一个适当的以 0 结尾的字符串。
  • @Ashwin : 如果你选择char a[] = "Network",VC++ 有什么要说的? (自动初始化)正如 unwind 所说,规范允许基于文字初始化数组,所以我猜它应该编译(没有 VC++ ..)
  • 放松:谢谢!正如@nos 在评论(上面)中指出的那样,它看起来确实像 C 和 C++ 之间的区别。一旦我在 VC++ 中将文件更改为 main.c,它就可以毫无错误地编译。
【解决方案2】:

虽然unwind's answer 解释了为什么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

一般来说,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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

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

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

      所以我猜 GCC 只关心警告。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-15
        • 2021-07-02
        • 2015-01-31
        • 2011-05-15
        • 2017-06-14
        • 2011-12-11
        • 1970-01-01
        相关资源
        最近更新 更多