在不同的作用域中使用相同名称的变量

41

这段代码可以编译,但在Visual Studio中出现了运行时错误:

运行时检查失败#3-变量'x'被使用而没有初始化...

int x = 15;
int main()
{
    int x = x;
    return 0;
}

当我点击继续按钮时,程序恢复运行,但 x 的内容已经损坏(例如显示 -8556328 而不是 15),我不明白这种行为背后的原因。

为什么这段代码没有问题,而且 int 数组也被正确声明了?

const int x = 5;
int main()
{
     int x[x] = {1,2,3,4};
     return 0;
}

19
你标记了C和C++两个标签。你编译的是哪一个? - underscore_d
3
一些有趣的事实:gcc 4.8.4编译并且这个程序可以在打开-Wall -Wextra -pedantic的情况下运行。clang 7.0.0编译它,也可以直接运行。但是,如果在 int x=x;后添加printf("%d\n", x);(我猜任何对 x 的实际使用都可以), 编译器会给出更友好的警告:warning: variable 'x' is uninitialized when used within its own initialization [-Wuninitialized]gcc即使有printf并打印了0,仍然可以编译和运行。但是在通过valgrind运行程序时,会出现“Conditional jump or move depends on uninitialised value(s)” 的错误提示。 - Joakim
@Joakim: 有趣的结果,谢谢分享。GCC和Clang是符合规定的吗?也就是说这是标准未定义的行为吗? - underscore_d
1
@underscore_d - C++并不要求在未初始化的变量上进行任何诊断。如果变量分配后从未真正使用,编译器可以自由地优化掉该变量,尤其是对于未使用变量。事实上,“未定义行为”意味着编译器可以随心所欲地进行操作。 - Jirka Hanika
@underscore_d - 修正:只有在某处取地址时,读取未初始化的变量才不是UB。_“如果lvalue标识了一个具有自动存储期限的对象,该对象可以使用寄存器存储类别声明(从未取它的地址),并且该对象未初始化(未使用初始化程序声明,并且先前未对其进行分配),则行为是未定义的。”_即使取了地址,这个规则仍然允许读取变量两次并看到不同的值,甚至更糟的情况也可能发生。 - Jirka Hanika
显示剩余3条评论
4个回答

52

x 在等号左侧被定义。

因此在 x[x] 中,[x] 指的是全局变量,

而在 x = x; 中,x 隐藏了全局的 x 并从自身初始化 -> UB(未定义行为)。


35
当您声明一个新变量时,它的名称会在此处变为可见。
int x =
//     ^- there

因为此时变量完全声明,所以它的名称才有意义。此时,任何周围作用域中已经声明的变量都将被隐藏。


5
在C语言中没有范围解析运算符,因此您可能无法使用。
int x = x;

在你的程序中。

7
OP似乎不知道他们想要C还是C++的答案。后者支持作用域解析。 - underscore_d
@underscore_d,是的,C++支持而C语言不支持。 - Adi

1
请使用作用域解析运算符(::)告诉编译器你的“x”是真实的“x”。由于用户定义的名称被编译器修饰,以避免在其级别上产生歧义,因此这些只是编译器最适合使用的名称。请注意保留HTML标签。
int x = 15;// Real name = gui_x
int main()
{
    int x = x;// lui_x
    return 0;
}

以这种方式,运行时将知道您正在使用哪个版本,但为避免歧义,它希望您使用特定的名称。有时会出现上述问题,您不知道自己正在使用已经使用过的名称。因此,C++创建了SRO。
现在,在数组x的情况下,x是地址而不是存储某些东西的整数,这就是编译器没有混淆的原因。您需要编写:
namespace abc //now all global variables are belongs to this ns abc
int x = 15;// Real name = gui_x
int main()
{
int x = abc::x;// lui_x
return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接