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 上有完整的记录。
考虑以下 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 上有完整的记录。