【发布时间】:2012-10-01 02:35:48
【问题描述】:
总的来说,我似乎对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 以生成赋值表达式的值?
更新:
所以,这就是两难境地。
如果“赋值后对象的值”不是从对易失性对象的额外读取中得到的,那么编译器就假设易失性对象b:
- 能够保存写入其中的任意
int值,它可能不是(例如,位 0 硬连线为 0,这对于硬件寄存器来说并不罕见,我们应该为此使用挥发物) - 无法在发生分配写入的时间点和获得表达式值的时间点之间更改(同样可能是硬件寄存器的问题)
因此,如果不是从 volatile 对象的额外读取中获得表达式值,则不会产生 volatile 对象的值,标准声称应该是这种情况。
这两个假设似乎都不太符合易变对象的性质。
如果,OTOH,“赋值后对象的值”是从对所述易失性对象的额外隐含读取中获得的,那么使用易失性左操作数评估赋值表达式的副作用取决于是否使用表达式值或不是或完全是任意的,这将是一种奇怪、出乎意料且记录不充分的行为。
【问题讨论】:
-
我希望对 volatile 的赋值并不意味着在存储之后进行读取(赋值的值是否在其他地方使用并不重要)。这意味着根据标准对
volatile的定义,将硬件寄存器映射到 volatile 变量(或指向 volatile 的指针)不会具有几乎每个人都依赖的语义。 -
@MichaelBurr 好点。