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


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

此代码繁忙,等待变量转到1。如果第一次通过vfoo没有设置为1,我会被困在里面吗。

此代码编译时没有任何警告。标准对此有何规定?

  • vfoo被声明为volatile。因此,对该变量的读取应而不是进行优化
  • 但是,bar不符合volatile的要求。是否允许编译器优化对此bar的写入。即编译器将对vfoo进行读取访问并且被允许丢弃该值而不将其分配给bar(在L.7(
  • 如果这是一个特殊情况,标准有话要说,你能加入条款并解释标准的律师谈话吗

标准对此的说明包括:

5.1.2.3程序执行

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

¶4在抽象机器中,所有表达式都按照语义的规定进行评估。如果一个实际实现可以推断出它的值没有被使用,并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用(,那么它就不需要计算表达式的一部分。

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

  • 对volatile对象的访问严格按照抽象机器的规则进行评估

特别是¶2的结论应该是,访问易失性对象与调用printf没有什么不同——它不能被忽略,因为它有副作用。想象一下你的程序用bar = printf("hellon");取代bar = vfoo;

任何访问都必须读取volatile变量。在您的代码片段中,无法优化read。编译器知道bar可能会受到副作用的影响。因此,条件将得到正确检查。

https://godbolt.org/z/nFd9BB

但是,bar不是volatile限定的。

变量bar用于保存一个值。你关心存储在其中的值,还是关心变量是否完全根据ABI表示?

易失性会保证你是后者。你的程序依赖于前者。

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

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

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

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

。即编译器将对vfoo进行读取访问,并被允许放弃该值,不将其分配给条形图(L.7(

当然不是!

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

可能是一个奇怪的CPU作为条件寄存器中的EQ1("等于1"(标志,每当加载等于1的值时就会设置该标志。然后编译器甚至不会临时存储读取值,而只存储EQ1条件测试。

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

最新更新