考虑以下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
struct变量清楚地隐藏了main中的a
struct变量。可以预期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 clang 3.4
答案 0 :(得分:9)
正如其他人所提到的,你正在阅读一个未初始化的局部变量,这是未定义的。所以,任何事都是合法的。话虽如此,这种行为有一个特殊原因: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完整记录。
答案 1 :(得分:4)
可以预期printf中的输出将是未定义的
未定义。未定义的行为意味着任何事情都可能发生,包括(但不限于)任何特定的输出。
在这种情况下,可能发生的事情是编译器优化a
两个地址。
答案 2 :(得分:4)
没有编译器错误,您的程序会调用未定义的行为。
您正在读取未初始化的自动对象,C表示它具有不确定的值,并且读取它是未定义的行为。
给a.x
(在main
范围内声明的那个)一个值,为程序指定一个特定的行为。
答案 3 :(得分:3)
这是一个GCC错误还是存在某种未触发的行为?
它未定义的行为。 s_t a;
语句中的if
隐藏了之前a
的声明
在块中
if (x > 0) {
s_t a;
a.x = x;
}
x
已分配给本地a.x
。在此阻止之后,a
不再可用,并且在printf
中您正在访问未初始化的变量。未初始化的变量可以调用UB。