2012-10-01 62 views
3

我似乎总体上对volatiles有一个合理的理解,但有一个看似不明确的情况,我不确定每个标准应该如何工作。我已经阅读了C99的相关部分以及SO上的十几个或更多相关帖子,但无法找到这种情况下的逻辑或解释这种情况的地方。赋值表达式和volatile

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

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;事情很清楚。但是上面的那些呢?

基本上,问题是,究竟是什么“表达有分配后左操作数的值”是指在C99的6.5.16c3当对象是挥发性?:

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

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

UPDATE

所以,这里的窘境。

如果未从易失性物体的额外的读获得了“分配后的对象的值”,则编译器作出的假设是,挥发性对象b

  • 是能够保持任意int值写入它,它可能不是(比方说,0位是硬连线到0,这与硬件寄存器,这是不寻常的事情,我们应该使用挥发物)
  • 不能改变之间的指定写入发生时的时间点和获取表达式值的时间点(并且可能是一个问题与硬件寄存器)

的,因为所有的和,表达值,如果没有从所述易失性物体的额外的读得到,不会产生挥发性物体,其标准权利要求应该是这样的值。

这两个假设似乎都不符合易失物体的性质。

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

+0

我希望赋值给volatile并不意味着在store之后进行读取(无论赋值的值是否在其他地方使用都无关紧要)。这意味着标准对volatile变量的映射硬件寄存器定义为volatile变量(或指向volatile的变量)不会有几乎所有人都依赖的语义。 –

+0

@MichaelBurr好点。 –

回答

2

C11澄清这是未定义的。

您可以找到C11的最终草稿here。现在引用的第二句是指脚注111:

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

脚注111这样说:

111)的执行被允许读取的对象以确定所述值,但不要求,即使当物体具有volatile限定类型。

+0

不错!所以,基本上,应该避免我在问题中提出的那种代码,除非可以容忍特定于实现的行为。 –

+0

@AlexeyFrunze:我认为这是不可避免的。设想一个硬件地址,它可以读取上次写入的任何值的平方根。现在想象一个用N循环延迟来计算它的例子。我认为标准不能明确定义“赋值后左操作数的值”。它可能需要读取,但不能规定写入之后读取的速度有多快。所以避免执行特定行为的唯一方法是禁止读取。 –

+0

@SteveJessop阅读和阅读还是有区别的。 C99-应该在这里更加清晰。 –