请考虑以下代码:
#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
,这是优化的。为什么这样,我怎么能避免它?
答案 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函数,
- 访问或修改易失性对象,或
- 执行同步操作或原子操作。
[注意:这是为了允许编译器转换,例如删除空循环,即使无法证明终止也是如此。 - 结束说明]
因为你的循环不会执行这些操作,优化器可能会认为永远不会输入循环,并将其完全删除。