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

Ara*_*uhi 4 c optimization microcontroller volatile

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

优点:

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

缺点:

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

das*_*ght 13

编译器标志-O0绝不能替代正确使用volatile,因为编译器正确优化后不起作用的代码本身就会被破坏.你不希望破坏的代码让你看起来"工作",直到有人忘记抛出-O0开关.

根据代码中变量的总百分比,即使大型代码库需要许多volatile变量也是不寻常的.修复丢失的大型代码库volatile可能需要找到一些需要多个变量的战略位置volatile,并修复这几个,而不是采用"猎枪方法"并禁用所有优化.

  • 小代码库对volatile的需求很大,这是非常常见的.即在任何程序中进行任何形式的硬件相关编程.这个问题被标记为"微控制器",因此可以安全地假设代码库仅需要大约1000个易失性限定变量用于寄存器映射. (2认同)

P__*_*J__ 11

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

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

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

这里有一个例子:

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

int foo1(int z)
{
    return z + z + z + z;    
}
Run Code Online (Sandbox Code Playgroud)

和结果代码(-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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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