有关GCC Optimizer的问题以及为什么此代码总是返回42?

时间:2019-02-08 06:47:10

标签: c gcc optimization

我最近遇到一个错误,该错误在switch语句中初始化了一个变量。我开始更多地尝试这一点,并且意识到我不知道GCC在其中的一些优化中试图做什么。

Given this code:

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

2 个答案:

答案 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