I ran into an error recently where a variable was initialized inside a switch statement. I started playing around with this more and realize I don't know the first thing about what GCC is trying to do in some of these optimizations.
Given this code:
int main(int argc, char** argv) {
switch (argc) {
case 1000: return 42;
int y = 24;
default: return y;
}
return argc;
}
The generated code always returns 42. What is going on? Why does the int y = 24
muck everything up?
$ 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
Turning on optimization flags makes the compiler attempt to improve the performance and/or code size at the expense of compilation time and possibly the ability to debug the program.
GCC has a range of optimization levels, plus individual options to enable or disable particular optimizations. The overall compiler optimization level is controlled by the command line option -On, where n is the required optimization level, as follows: -O0 . (default).
Compiler specific pragma gcc provides pragma GCC as a way to control temporarily the compiler behavior. By using pragma GCC optimize("O0") , the optimization level can be set to zero, which means absolutely no optimize for gcc.
The compiler optimizes to reduce the size of the binary instead of execution speed. If you do not specify an optimization option, gcc attempts to reduce the compilation time and to make debugging always yield the result expected from reading the source code.
int main(int argc, char** argv) {
switch (argc) {
case 1000: return 42;
int y = 24;
default: return y;
}
return argc;
}
To explain this a bit more, a switch doesn't exactly do a linear progression. The logic equivalent to this would be:
"If argc is 1000, return 42. Otherwise return y"
The int y = 24;
is never used since it's never reached, the compiler can optimize this out, and since there's UB in the case of a default, it might as well return 42.
To fix this and behave the way I suspect you intend, you just need to declare y
outside of the switch statement.
int main(int argc, char** argv) {
int y = 24;
switch (argc) {
case 1000: return 42;
default: return y;
}
return argc;
}
Cases in a switch
are to be regarded as labels. If we translate your code to the equivalent goto-spaghetti, it might be easier to understand:
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;
}
The goto label_default
jumps past the label initialization of y
and so it doesn't necessarily get executed. The same thing happens in your switch.
Best practice when declaring variables inside switches is therefore to always use a compound statement per case:
case 1000:
{
int y = 24;
break;
}
Apart from preventing spaghetti bugs, this also reduces the scope of the variable to the specific case
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With