简体   繁体   中英

Question about GCC Optimizer and why this code always returns 42?

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
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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM