总的来说,我似乎有一个合理的理解volatiles
,但是有一个看似晦涩的案例,我不确定按照标准应该如何工作。我已经阅读了 C99 的相关部分以及关于 SO 的十几篇或更多相关帖子,但找不到此案例的逻辑或解释此案例的地方。
假设我们有这段代码:
int a, c;
volatile int b;
a = b = 1;
c = b += 1; /* or equivalently c = ++b; */
应该a
这样评价:
b = 1;
a = b; // volatile is read
或像这样:
b = 1;
a = 1; // volatile isn't read
?
同样,应该c
这样评估:
int tmp = b;
tmp++;
b = tmp;
c = b; // volatile is read
或像这样:
int tmp = b;
tmp++;
b = tmp;
c = tmp; // volatile isn't read
?
在简单的情况下,a = b; c = b;
事情很清楚。但是上面的那些呢?
基本上,问题是,当对象为 volatile 时,C99 的 6.5.16c3 中的“表达式在赋值后具有左操作数的值”到底是什么意思?:
赋值运算符将值存储在左操作数指定的对象中。赋值表达式在赋值之后具有左操作数的值,但不是左值。
它是否意味着额外读取 volatile 以产生赋值表达式的值?
更新:
所以,这就是两难境地。
如果“赋值后对象的值”不是从对 volatile 对象的额外读取中获得的,那么编译器会假设 volatile 对象b
:
- 能够保存写入其中的任意
int
值,它可能不是(例如,位 0 硬连线为 0,这对于硬件寄存器来说并不罕见,我们应该使用 volatiles) - 在分配写入发生的点和获得表达式值的点之间不能改变(这可能是硬件寄存器的问题)
正因为如此,如果不是从 volatile 对象的额外读取中获得表达式值,则不会产生 volatile 对象的值,标准声称应该是这种情况。
这两个假设似乎都不太适合易失性对象的性质。
如果,OTOH,“赋值后对象的值”是从对所述易失性对象的额外隐含读取中获得的,那么使用易失性左操作数评估赋值表达式的副作用取决于是否使用表达式值完全任意,这将是一种奇怪的、意外的和记录不充分的行为。