代码之家  ›  专栏  ›  技术社区  ›  Goswin von Brederlow

为什么不初始化而不越界?

  •  45
  • Goswin von Brederlow  · 技术社区  · 7 年前

    在下面的代码中,为什么是 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;
    }
    

    编译器调用:

    % 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.
    

    更新:现在这很奇怪:

    #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;
    }
    

    编译此命令将导致出现人们所期望的警告:

    % 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]);
    

    突然,海湾合作委员会看到了它是什么的界限。

    4 回复  |  直到 7 年前
        1
  •  57
  •   Antti Haapala -- Слава Україні    7 年前

    我相信这里可能是这样:在第一个代码中,gcc注意到您根本不需要整个char数组,只是 b[9] ,因此它可以用

    char b_9; // = ???
    printf("b[9] = %d\n", b_9);
    

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

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


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

        2
  •  17
  •   Bathsheba    7 年前

    阅读行为 b[9] b[10] 未定义 .

    您的编译器正在发出一个警告(它不必发出),尽管警告文本有点误导性,但技术上并没有错误。在我看来,这相当聪明。(C编译器是 需要发出越界访问诊断。)

    关于 &b[9] ,编译器是 允许取消引用,并且必须将其评估为 b + 9 . 允许您将指针设置为一个超过数组结尾的指针。将指针设置为 &b[10] 未定义 .

        3
  •  1
  •   chux    7 年前

    一些额外的实验结果。


    使用 char b[9] 而不是 char b[] 似乎没有什么不同,海湾合作委员会仍然警告同 字符B[9] .

    有趣的是,通过中的“next”成员初始化一个传递的元素 struct 1)不消除“未初始化”警告,2)不警告是否访问数组外部。

    #include <stdio.h>
    
    typedef struct {
      char c[9];
      char d[9];
    } TwoNines;
    
    int main(void) {
      char b[9] = { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' };
      printf("b[] size %zu\n", sizeof b);
      printf("b[9] = %d\n", b[9]);   // 'b[9]' is used uninitialized in this function [-Wuninitialized]
    
      TwoNines e = { { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' }, //
                     { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' } };
    
      printf("e size %zu\n", sizeof e);
      printf("e.c[9] = %d\n", e.c[9]);   // No warning.
    
      return 0;
    }
    

    产量

    b[] size 9
    b[9] = 0
    e size 18    // With 18, we know `e` is packed.
    e.c[9] = 78  // 'N'
    

    笔记:
    GCC-标准=C11-O3-G3-踏板-墙-WEXTRA-wconversion-C-fmessage length=0-V-mmd-mp…
    通用条款/GCC-7.3.0-2.I686

        4
  •  -2
  •   0___________    7 年前

    当您使用-o2编译代码时,示例的琐碎性使这个变量得到了优化。所以警告是100%正确的