Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么未初始化而不是越界?_C_Gcc_Gcc Warning - Fatal编程技术网

C 为什么未初始化而不是越界?

C 为什么未初始化而不是越界?,c,gcc,gcc-warning,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; } 更新:现在这很奇怪: #include <stdio.h> void foo(char *); int main(void

在下面的代码中,为什么
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;
}
更新:现在这很奇怪:

#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发现了它的界限。

读取
b[9]
b[10]
的行为未定义

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


关于
&b[9]
,编译器不允许取消引用它,并且必须将其计算为
b+9
。您可以将指针设置为超过数组末尾1。设置指向
&b[10]
的指针的行为是未定义的。

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

char b_9; // = ???
printf("b[9] = %d\n", b_9);
现在,这是一个完全合法的转换,因为当数组被越界访问时,行为是完全未定义的。只有在后一阶段,它才会注意到这个变量(它是
b[9]
的替代物)未初始化,并发出诊断消息

我为什么相信这一点?因为如果我只添加任何引用内存中数组地址的代码,例如
printf(“%p\n”,&b[8])任何地方,数组现在都在内存中完全实现,编译器将诊断数组下标是否高于数组边界



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

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

一些额外的实验结果


使用
char b[9]
而不是
char b[]
似乎没有什么区别,gcc仍然会对
char b[9]
发出相同的警告

有趣的是,通过
结构中的“next”成员初始化一个传递的元素(1)会消除“uninitialized”警告,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;
}

注:
gcc-std=c11-O3-g3-pedantic-Wall-Wextra-Wconversion-c-fmessage length=0-v-MMD-MP…

gcc/gcc-7.3.0-2.i686

有趣的是把它敲对了。试着
printf(“b[10]=%d\n”,b[10])9是数组末尾的一个地址,是一个允许的地址(尽管它仍然没有定义以实际取消引用它…。@AndrewHenle但是b[9]是一个取消引用,&b[9]将是有效的。更奇怪的是,超过数组末尾的一个可能会得到不同的处理——在你的第一个例子中,不是很正确。请参阅C标准中有关指针算法的段落:不同的警告可能来自不同的gcc版本。这两个示例的行为都未被标准定义,因此编译器实际上不需要对它们做任何特别的操作—不需要警告。编译器开发人员面临的问题是,未定义的行为可能以无数种方式表现出来。因此,编译器很难快速(在程序员不抱怨编译时间太长的意义上)计算出哪个警告是“最好的”。问题是关于gcc显示的警告。与代码的行为或有效性无关。Gcc现在不考虑越界错误,问题是为什么不在这里使用它。有关更多奇怪之处,请参阅更新。@GoswinvonBrederlow:gcc警告是正确的。b[9]是未初始化的。它确实不是未初始化的。这是不允许的。取消对它的引用显然是错误的。@GoswinvonBrederlow:我们可以同意在这一点上存在分歧。您的问题现在更有趣了,因为您添加了&b[9]&c。True
b[9]
未初始化。它也在
b[]
数组之外。“初始化”
b[9]
并且仍然只有9个元素。结果支持“警告文本有点误导,但在技术上并不错误。”重新访问“下一个”成员:这是合法的,因为:1。指向第一个成员的指针相当于指向整个结构2的指针。任何对象都可以安全地转换为char数组,并取消引用以检查字节表示,3
e.c
碰巧是一个字符数组,所以4。你正在将整个结构输入一个字符数组,我同意。gcc-O0代码速度慢,不可读,跳过很多警告,与您想要发布的任何东西都没有什么关联。您应该始终使用-Os或-O2。
char b_9; // = ???
printf("b[9] = %d\n", b_9);
#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'