为什么没有初始化而不是越界?

Gos*_*low 45 c gcc gcc-warning

在下面的代码中为什么b[9]未初始化而不是越界?

#include <stdio.h>

int main(void)
{
    char b[] = {'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!'};
    printf("b[9] = %d\n", b[9]);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译器调用:

% gcc -O2 -W -Wall -pedantic -c foo.c
foo.c: In function ‘main’:
foo.c:6:5: warning: ‘b[9]’ is used uninitialized in this function [-Wuninitialized]
     printf("b[9] = %d\n", b[9]);
% gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Run Code Online (Sandbox Code Playgroud)

更新:现在这很奇怪:

#include <stdio.h>

void foo(char *);

int main(void)
{
    char b[] = {'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!'};
    foo(&b[9]);
    foo(&b[10]);
    printf("b[9] = %d\n", b[9]);
    printf("b[10] = %d\n", b[10]);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译这会导致人们期望的警告:

% gcc -O2 -W -Wall -pedantic -c foo.c
foo.c: In function ‘main’:
foo.c:9:5: warning: array subscript is above array bounds [-Warray-bounds]
     foo(&b[10]);
     ^
foo.c:10:29: warning: array subscript is above array bounds [-Warray-bounds]
     printf("b[9] = %d\n", b[9]);
                             ^
foo.c:11:29: warning: array subscript is above array bounds [-Warray-bounds]
     printf("b[10] = %d\n", b[10]);
Run Code Online (Sandbox Code Playgroud)

突然间,gcc看到了它的出界.

Ant*_*ala 56

我相信这可能是这样的:在第一个代码中,GCC注意到你根本不需要整个char数组b[9],所以它可以用

char b_9; // = ???
printf("b[9] = %d\n", b_9);
Run Code Online (Sandbox Code Playgroud)

现在,这是一个完全合法的转换,因为当数组被越界访问时,行为是完全未定义的.只有在后一阶段才会注意到这个替代的变量b[9]是未初始化的,并发出诊断消息.

为什么我相信这个?因为如果我添加任何将在内存中引用数组地址的代码,例如在任何地方,数组现在完全在内存中实现,编译器将诊断数组下标在数组边界之上.printf("%p\n", &b[8]);


我发现更有趣的是,除非启用了优化,否则GCC根本不会诊断出越界访问.这将再次表明,无论何时编写程序新程序,都应该在启用优化的情况下编译它,以使错误高度可见,而不是使用调试模式隐藏它们;)


Bat*_*eba 16

在阅读行为b[9]或者b[10]不确定的.

您的编译器正在发出警告(它不必),尽管警告文本有点误导,但在技术上不正确.在我看来,它相当聪明.(AC是编译器要求出具诊断是否越界进入.)

至于&b[9],编译器不会允许取消引用这一点,必须评估它b + 9.您可以在数组末尾设置一个指针.设置指针的行为&b[10]未定义的.

  • 明显.问题是关于gcc显示的警告.不是关于代码的行为或有效性.Gcc现在关于越界错误,问题是它为什么不在这里使用它.请参阅更新以获得更多陌生感. (2认同)
  • @GoswinvonBrederlow:gcc警告是正确的.b [9]未初始化. (2认同)