我最近遇到一个错误,该错误在switch语句中初始化了一个变量。我开始更多地尝试这一点,并且意识到我不知道GCC在其中的一些优化中试图做什么。
int main(int argc, char** argv) {
switch (argc) {
case 1000: return 42;
int y = 24;
default: return y;
}
return argc;
}
生成的代码始终返回42。这是怎么回事?为什么int y = 24
搞砸了一切?
$ gcc -Wall -Werror -O2 -c test.c
$ objdump -drwCS -Mintel test.o
testo.o: file format elf64-x86-64
Disassembly of section .text.startup:
0000000000000000 <main>:
0: b8 2a 00 00 00 mov eax,0x2a
5: c3 ret
答案 0 :(得分:4)
int main(int argc, char** argv) {
switch (argc) {
case 1000: return 42;
int y = 24;
default: return y;
}
return argc;
}
为进一步说明这一点,开关并不能完全线性地进行。与此等效的逻辑是:
“如果argc为1000,则返回42。否则返回y”
int y = 24;
从未使用过,因为它从未达到过,编译器可以对其进行优化,并且由于在默认情况下存在UB,它也可能返回42。
要解决此问题并按我怀疑的方式运行,只需在switch语句之外声明y
。
int main(int argc, char** argv) {
int y = 24;
switch (argc) {
case 1000: return 42;
default: return y;
}
return argc;
}
答案 1 :(得分:4)
switch
中的案件应视为标签。如果我们将您的代码翻译为等效的goto-spaghetti,可能会更容易理解:
int main(int argc, char** argv)
{
if(argc == 1000)
goto label_1000;
else
goto label_default;
label_1000: return 42;
int y = 24;
label_default: return y;
return argc;
}
goto label_default
跳过了y
的标签初始化,因此不一定要执行它。同一件事在您的交换机中发生。
因此,在开关内部声明变量的最佳实践是始终在每种情况下使用复合语句:
case 1000:
{
int y = 24;
break;
}
除了防止意大利面条错误外,这还将变量的范围缩小到特定的case
。