GCC 中结构变量的作用域规则

Scoping rules for struct variables in GCC

考虑以下 C 程序

#include <stdio.h>

typedef struct s {
  int x;
} s_t;

int main() {
  int x;
  s_t a;

  scanf("%d", &x);

  if (x > 0) {
    s_t a;
    a.x = x;
  }

  printf("%d\n", a.x);
}

if 分支中的 a 结构变量明显隐藏了 main 中的 a 结构变量。人们会期望 printf 中的输出是未定义的,但是对于 GCC,作用域变量似乎等于主变量。

例如

gcc test.c -o test
echo 10 | ./test

将输出 10。

另一方面,运行 这通过 clang,如预期的那样

clang test.c -o test
echo 10 | ./test

输出-2145248048。

这是 GCC 错误还是触发了某种未定义的行为?

gcc 4.8.2 铿锵声 3.4

One would expect that the output in printf would be undefined

未定义。未定义的行为意味着任何事情都可能发生,包括(但不限于)任何特定的输出。

在这种情况下,可能发生的情况是编译器将两者优化 a 以具有相同的地址。

Is this a GCC bug or is there some sort of undefined behavior that this is triggering?

这是未定义的行为。 if语句中的s_t a;隐藏了a之前的声明。
在区块

if (x > 0) {
    s_t a;
    a.x = x;
}  

x赋值给本地a.x。在此块之后,a 不再可用,在 printf 中您正在访问未初始化的变量。未初始化的变量可能会调用 UB。

没有编译器错误,您的程序调用了未定义的行为。

您正在读取一个未初始化的自动对象,C 表示它具有不确定的值并且读取它是未定义的行为。

a.x(在 main 范围内声明的那个)一个值,使您的程序具有指定的行为。

正如其他人所提到的,您正在读取一个未初始化的局部变量并且它是未定义的。所以,任何事情都是合法的。话虽如此,这种行为有一个特殊的原因:gcc 尽可能多地重用堆栈上的变量(即,只要生成的代码可证明是正确的)。您可以使用 -fstack-reuse 选项进行微调。

要禁用堆栈重用:

gcc test.c -o test -fstack-reuse=none
echo 10 | ./test
4195808 # prints some random number.

为所有变量启用堆栈重用:

gcc test.c -o test -fstack-reuse=all  #, or -fstack-reuse=named_vars
echo 10 | ./test
10 # prints 10, as it reuses the space on the stack.

这在 GCC Code Generation Options 上有完整的记录。