赋值表达式和 volatile

Ale*_*nze 5 c volatile

我似乎对一般情况有一个合理的理解volatiles,但有一个看似晦涩的案例,我不确定按照标准应该如何工作。我已经阅读了 C99 的相关部分以及关于 SO 的十几个或更多相关帖子,但找不到这种情况下的逻辑或解释这种情况的地方。

假设我们有这样一段代码:

  int a, c;
  volatile int b;
  a = b = 1;
  c = b += 1; /* or equivalently c = ++b; */
Run Code Online (Sandbox Code Playgroud)

应该a这样评价:

  b = 1;
  a = b; // volatile is read
Run Code Online (Sandbox Code Playgroud)

或者像这样:

  b = 1;
  a = 1; // volatile isn't read
Run Code Online (Sandbox Code Playgroud)

?

同样,应该c这样评估:

  int tmp = b;
  tmp++;
  b = tmp;
  c = b; // volatile is read
Run Code Online (Sandbox Code Playgroud)

或者像这样:

  int tmp = b;
  tmp++;
  b = tmp;
  c = tmp; // volatile isn't read
Run Code Online (Sandbox Code Playgroud)

?

在简单的情况下,a = b; c = b;事情就很清楚了。但是上面的那些呢?

基本上,问题是,当对象是 volatile 时,C99 的 6.5.16c3 中“表达式具有赋值后左操作数的值”究竟是什么意思?:

赋值运算符在左操作数指定的对象中存储一个值。赋值表达式具有赋值后左操作数的值,但不是左值。

它是否意味着额外读取 volatile 以生成赋值表达式的值?

更新

所以,这就是困境。

如果“赋值后对象的值”不是从 volatile 对象的额外读取中获得的,那么编译器会假设 volatile 对象b

  • 能够保存int写入其中的任意值,它可能不是(例如,位 0 被硬连接到 0,这对于硬件寄存器来说并不罕见,我们应该使用 volatiles)
  • 无法在分配写入发生的时间点和获得表达式值的时间点之间更改(这也可能是硬件寄存器的问题)

由于所有这些,如果不是从 volatile 对象的额外读取中获得的表达式值,则不会产生 volatile 对象的值,标准声称应该是这种情况。

这两个假设似乎都不太适合易失性对象的性质。

如果,OTOH,“赋值后对象的值”是从所述易失性对象的额外隐含读取中获得的,那么使用易失性左操作数评估赋值表达式的副作用取决于是否使用了表达式值完全任意的,这将是一种奇怪的、意外的和记录不足的行为。

rob*_*off 5

C11 澄清,这一点尚未明确。

您可以在此处找到 C11 的最终草案。你现在引用的第二句话指的是脚注111:

赋值运算符将值存储在左操作数指定的对象中。赋值表达式具有赋值后左操作数的值,111),但不是左值。

脚注 111 说:

  1. 允许实现读取对象来确定值,但不要求这样做,即使对象具有 易失性限定类型。