-O0编译器标志与C中的volatile关键字具有相同的效果吗?

在C中使用-O0编译器标志时,告诉编译器避免任何类型的优化.将变量定义为volatile时,告诉编译器避免优化该变量.我们可以互换使用这两种方法吗?如果是这样,有什么优点和缺点?以下是我能想到的一些优点和缺点.还有吗?

优点:

>如果我们有一个很大的代码库,其中应该声明为volatile的变量不是,那么使用-O0标志会很有帮助.如果代码显示错误行为,而不是进入代码并找到哪些变量需要声明为volatile,我们可以使用-O0标志来消除优化导致问题的可能性.

缺点:

> -O0标志将影响整个代码,而volatile关键字仅影响特定变量.例如,如果我们正在研究一个小型微控制器,这可能是一个问题,因为使用-O0可能会产生一个大的可执行文件.

最佳答案
简短的回答是:volatile关键字并不意味着“不优化”.这是完全不同的东西.它通知编译器变量可能被正常程序流中的编译器不可见的东西改变.例如:

>它可以由硬件改变 – 通常是映射在存储器地址空间中的寄存器
>可以通过从未调用的函数进行更改 – 例如中断例程
>变量可以由另一个进程或硬件更改 – 例如多处理器/多核系统中的共享内存

每次使用volatile变量时都必须从其存储位置读取,并在每次更改时保存.

这里有一个例子:

int foo(volatile int z)
{
    return z + z + z + z;
}

int foo1(int z)
{
    return z + z + z + z;    
}

和结果代码(-O0优化选项)

foo(int):
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-4], edi
  mov edx, DWORD PTR [rbp-4]
  mov eax, DWORD PTR [rbp-4]
  add edx, eax
  mov eax, DWORD PTR [rbp-4]
  add edx, eax
  mov eax, DWORD PTR [rbp-4]
  add eax, edx
  pop rbp
  ret
foo1(int):
  push rbp
  mov rbp, rsp
  mov DWORD PTR [rbp-4], edi
  mov eax, DWORD PTR [rbp-4]
  sal eax, 2
  pop rbp
  ret

我认为差别很明显.读取volatile变量4次,读取非易失性一次,然后乘以4.

你可以在这里玩:https://godbolt.org/g/RiTU4g

在大多数情况下,如果在打开编译器优化时程序没有运行,则代码中会有一些隐藏的UB.您应该根据需要进行调试以发现所有这些.正确编写的程序必须在任何优化级别运行.

请记住,“易变”并不意味着或保证一致性&原子.

转载注明原文:-O0编译器标志与C中的volatile关键字具有相同的效果吗? - 代码日志