将易失性分配给非易失性语义和C标准

时间:2019-11-11 10:52:22

标签: c language-lawyer volatile non-volatile

volatile int vfoo = 0;
void func()
{
    int bar;
    do
    {
        bar = vfoo;  // L.7
    }while(bar!=1);
    return;
}

此代码忙于等待变量变为1。如果在第一遍vfoo上未设置为1,我会卡在里面吗。

此代码编译时没有警告。 标准对此有何说法?

  • vfoo被声明为volatile。因此,应优化对该变量的读取。
  • 但是,酒吧没有 volatile资格。是否允许编译器优化对此bar的写入?即编译器将对vfoo进行读取访问,并允许丢弃此值而不将其分配给bar(在L.7)。
  • 如果是在特殊情况下标准有话要说,您能否包括该条款并解释标准的律师谈话?

3 个答案:

答案 0 :(得分:3)

标准对此要说的包括:

  

5.1.2.3程序执行

     

¶2访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用,它们是执行环境状态的变化。对表达式的评估通常包括值计算和副作用的启动。左值表达式的值计算包括确定指定对象的身份。

     

¶4在抽象机中,所有表达式均按语义指定的方式求值。如果实际实现可以推断出未使用表达式的值并且没有产生所需的副作用(包括由调用函数或访问易失性对象引起的副作用),则无需对表达式的一部分进行求值。

     

¶6对一致性实现的最低要求是:

     
      
  • 严格根据抽象机的规则评估对易失对象的访问。
  •   
  • ...
  •   

尤其是从¶2中获得的收益应该是,访问易失性对象与调用printf之类的东西没有什么不同-由于它具有副作用,因此不能忽略它。想象一下,您的程序将bar = vfoo;替换为bar = printf("hello\n");

答案 1 :(得分:2)

volatile变量必须在任何访问中读取。在您的代码段中,无法优化读取。编译器知道bar可能会受到副作用的影响。这样就可以正确检查条件了。

https://godbolt.org/z/nFd9BB

答案 2 :(得分:0)

  

但是,bar不具有挥发性。

变量bar用于保存值。您是否在乎存储在其中的值,还是在乎该变量是否根据ABI精确表示?

易失性将向您保证后者。您的程序取决于前者。

  

是否允许编译器优化对此栏的写入?

当然。为什么您要可能关心所读取的值是否真的写入了分配给堆栈上变量的内存位置?

您所指定的只是将读取的值作为退出条件进行了测试:

        bar = ...
    }while(bar!=1);
  

。即编译器将对vfoo进行读取访问,并允许   丢弃此值,而不将其分配给bar(在L.7)。

当然不是!

编译器需要保留volatile读取所获得的值足够的时间,以便能够将其与1进行比较。但是,您再也不需要了,因为您以后再也不会使用bar了。

可能是一个奇怪的CPU作为条件寄存器中的EQ1(“等于1”)标志,每当装入等于1的值时就将其置位。这样,编译器甚至不会临时存储读取值,而仅存储EQ1条件测试。

根据您的假设,编译器可以丢弃所有非易失性变量的变量值,非易失性对象几乎没有任何用途

相关问题