【问题标题】:assignment expressions and volatile赋值表达式和 volatile
【发布时间】: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 好点。

标签: c volatile


【解决方案1】:

C11 澄清这是未指定的。

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

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

脚注 111 说:

  1. 允许实现读取对象以确定值,但不是必需的,即使对象具有 volatile 限定类型。

【讨论】:

  • 不错!因此,基本上,除非可以容忍特定于实现的行为,否则应该避免我在问题中提出的那种代码。
  • @AlexeyFrunze:我认为这是不可避免的。想象一个硬件地址,它读取为最后写入那里的任何值的平方根。现在想象一个用 N 周期延迟来计算它的人。我认为标准没有任何方式可以明智地定义“赋值后左操作数的值”。它可能需要读取,但它不能规定读取在写入之后的速度。因此,避免特定于实现的行为的唯一方法是禁止读取。
  • @SteveJessop 阅读和不阅读还是有区别的。 C99- 应该在这里更清楚。
  • @Alexey:同意。当下一个标准澄清某些事情时,这意味着上一个标准不够清楚(在 C11 作者看来):-)
  • @rob:你说这是在 C11 中实现定义的。是吗?只是看到你引用的文字,我会说它是未指定的。区别在于实现是否必须记录一种让程序员知道它会做什么的方法。
【解决方案2】:

根据常识,我会这样争论:

如果b = (whatever)whatever 可以存储在寄存器中,则编译器没有理由重新评估表达式以进行赋值。 也因为它不能比寄存器中的值更新。 考虑f(x)r = f(x):一旦知道f(x) 的结果,就可以对其进行分配。 所以对于a = b = 1,应该没有理由再次将1 分配给b,只是为了能够分配给a。 还假设你写a = ++b: 显然b 不能第二次递增;否则会破坏基本的 C 语义。

【讨论】:

  • -1。上面已经回答了。而且您忽略了标准,而问题主要围绕它,围绕合规行为。可以进行非常明显的优化,但问题是它是必需的、非法的还是可选的。
  • 其实我觉得我的回答还是有帮助的。
猜你喜欢
  • 2022-08-03
  • 2012-07-23
  • 2019-09-03
  • 1970-01-01
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多