是GCC的选择-O2打破这个小程序还是我有未定义的行为

时间:2014-05-15 15:47:19

标签: c gcc compiler-optimization inline-functions

我在一个非常大的应用程序中发现了这个问题,从中做了一个SSCCE。我不知道代码是否有未定义的行为或-O2打破它。

使用gcc a.c -o a.exe -O2 -Wall -Wextra -Werror进行编译时,会打印 5

但是在没有-O2(例如-O1)的情况下进行编译时会打印 25 ,或者取消注释2条注释行中的一条(防止内联)。

#include <stdio.h>
#include <stdlib.h>
// __attribute__((noinline)) 
int f(int* todos, int input) {
    int* cur = todos-1; // fixes the ++ at the beginning of the loop
    int result = input;
    while(1) {
        cur++;
        int ch = *cur;
        // printf("(%i)\n", ch);
        switch(ch) {
            case 0:;
                goto end;
            case 1:;
                result = result*result;
            break;
        }
    }
    end:
    return result;
}
int main() {
    int todos[] = { 1, 0}; // 1:square, 0:end
    int input = 5;
    int result = f(todos, input);
    printf("=%i\n", result);
    printf("end\n");
    return 0;
}

GCC的选项-O2打破了这个小程序,还是某个地方有未定义的行为?

2 个答案:

答案 0 :(得分:13)

int* cur = todos-1;

调用未定义的行为。 todos - 1是无效的指针地址。

强调我的:

  

(C99,6.5.6p8)“如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义。

答案 1 :(得分:1)

作为@ ouah的回答的补充,这解释了编译器正在做什么。

生成汇编程序以供参考:

  400450:       48 83 ec 18             sub    $0x18,%rsp
  400454:       be 05 00 00 00          mov    $0x5,%esi
  400459:       48 8d 44 24 fc          lea    -0x4(%rsp),%rax
  40045e:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)
  400465:       00 
  400466:       48 83 c0 04             add    $0x4,%rax
  40046a:       8b 10                   mov    (%rax),%edx

但是,如果我在printf中添加main()

  400450:       48 83 ec 18             sub    $0x18,%rsp
  400454:       bf 84 06 40 00          mov    $0x400684,%edi
  400459:       31 c0                   xor    %eax,%eax
  40045b:       48 89 e6                mov    %rsp,%rsi
  40045e:       c7 04 24 01 00 00 00    movl   $0x1,(%rsp)
  400465:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)
  40046c:       00 
  40046d:       e8 ae ff ff ff          callq  400420 <printf@plt>
  400472:       48 8d 44 24 fc          lea    -0x4(%rsp),%rax
  400477:       be 05 00 00 00          mov    $0x5,%esi
  40047c:       48 83 c0 04             add    $0x4,%rax
  400480:       8b 10                   mov    (%rax),%edx

具体而言(在printf版本中),这两条指令填充了todo数组

  40045e:       c7 04 24 01 00 00 00    movl   $0x1,(%rsp)
  400465:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)

printf版本显然遗漏了这个版本,由于某些原因,它只分配第二个元素:

  40045e:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%rsp)