为什么GCC会优化这种增量?

时间:2015-04-17 19:53:33

标签: c++ gcc optimization

请考虑以下代码:

#include <stdlib.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>

uint64_t counter = 0;

#define __STDC_FORMAT_MACROS
#include <inttypes.h>

void sig_handler(int signo) {
   printf( "%" PRIu64 "\n", counter);

}

int main() {
    struct sigaction act;
    act.sa_handler = &sig_handler;
    sigaction(SIGINT, &act, NULL);

    for( ;; ) {
        counter++;
    }
    return 0;
}

如果我使用-O0编译代码,我可以看到当我按CTR + C时计数器会增加。使用-O1,这是优化的。为什么这样,我怎么能避免它?

2 个答案:

答案 0 :(得分:4)

看起来C ++ 11标准草案的以下部分是相关部分1.9 [intro.execution]

  

当抽象机器的处理因收据而中断时   一个信号,对象的值既不是

     
      
  • 类型为volatile std :: sig_atomic_t也不是
  •   
  • 无锁原子对象(29.4)
  •   
     在执行信号处理程序期间未指定

  不属于这两个类别的任何对象的值   由处理程序修改变为未定义。

由于counter volatile 原子对象,因此未指定该值,因此允许编译器通过{{3}来优化它}。

C ++ 14草案中的措辞有所改变,我们有以下内容:

  

如果由于调用raise函数而执行了一个信号处理程序,那么处理程序的执行就是   在调用raise函数之后和返回之前排序。 [注意:收到信号时   另一个原因是,信号处理程序的执行通常与其余部分无关   程序。 - 后注]

这似乎在某种意义上没有指定,因为它只是说明序列处理程序未被排序的注释,但如果我们阅读as-if rule我们可以看到这似乎被认为是数据竞争和因此未定义的行为。

答案 1 :(得分:1)

根据1.10节中的进度保证规则:

,您的代码会显示未定义的行为
  

实现可能假设任何线程最终将执行以下操作之一:

     
      
  • 终止,
  •   
  • 调用库I / O函数,
  •   
  • 访问或修改易失性对象,或
  •   
  • 执行同步操作或原子操作。
  •   
     

[注意:这是为了允许编译器转换,例如删除空循环,即使无法证明终止也是如此。 - 结束说明]

因为你的循环不会执行这些操作,优化器可能会认为永远不会输入循环,并将其完全删除。