我似乎有一个合理的理解volatiles
一般来说,但有一个看似晦涩的案例,我不确定事情应该如何按照标准进行工作。我已经阅读了 C99 的相关部分以及 SO 上的十几个或更多相关帖子,但找不到这种情况下的逻辑或解释这种情况的地方。
假设我们有这段代码:
int a, c;
volatile int b;
a = b = 1;
c = b += 1; /* or equivalently c = ++b; */
Should 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;
事情很清楚。但是上面的那些呢?
基本上,问题是,当对象是易失性时,在 C99 的 6.5.16c3 中“表达式具有赋值后左操作数的值”究竟意味着什么?:
赋值运算符将值存储在由
左操作数。一项作业表达式具有左操作数的值
分配后,但不是左值。
这是否意味着额外读取 volatile 来生成赋值表达式的值?
UPDATE:
所以,这就是困境。
如果从 volatile 对象的额外读取中没有获得“赋值后的对象的值”,那么编译器会假设 volatile 对象b
:
- 能够持有任意
int
写入其中的值,也可能不是(例如,位 0 被硬连线为 0,这对于硬件寄存器来说并不罕见,我们应该使用易失性)
- 在发生分配写入的点和获得表达式值的点之间不能改变(这也可能是硬件寄存器的问题)
正因为如此,如果表达式值不是从 易失性对象的额外读取中获得的,则不会产生易失性对象的值,而标准声称应该是这种情况。
这两个假设似乎都不太符合易失性对象的性质。
OTOH,如果“赋值后对象的值”是从所述易失性对象的额外隐式读取中获得的,那么使用易失性左操作数计算赋值表达式的副作用取决于表达式值是否被使用或是否被使用。完全任意,这将是一种奇怪的、意外的且记录不足的行为。