C 赋值表达式与volatile
总的来说,我似乎对挥发性物质有一个合理的理解,但有一个似乎不太清楚的例子,我不确定按照标准应该如何工作。我已经阅读了C99的相关部分和十几篇或更多的相关文章,但找不到本案例的逻辑或解释本案例的地方 假设我们有一段代码:C 赋值表达式与volatile,c,volatile,C,Volatile,总的来说,我似乎对挥发性物质有一个合理的理解,但有一个似乎不太清楚的例子,我不确定按照标准应该如何工作。我已经阅读了C99的相关部分和十几篇或更多的相关文章,但找不到本案例的逻辑或解释本案例的地方 假设我们有一段代码: int a, c; volatile int b; a = b = 1; c = b += 1; /* or equivalently c = ++b; */ a是否应该这样评估: b = 1; a = b; // volatile is read
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
int tmp = b;
tmp++;
b = tmp;
c = b; // volatile is read
int tmp = b;
tmp++;
b = tmp;
c = tmp; // volatile isn't read
或者像这样:
b = 1;
a = b; // volatile is read
b = 1;
a = 1; // volatile isn't read
int tmp = b;
tmp++;
b = tmp;
c = b; // volatile is read
int tmp = b;
tmp++;
b = tmp;
c = tmp; // volatile isn't read
?
类似地,c
应该这样评估:
b = 1;
a = b; // volatile is read
b = 1;
a = 1; // volatile isn't read
int tmp = b;
tmp++;
b = tmp;
c = b; // volatile is read
int tmp = b;
tmp++;
b = tmp;
c = tmp; // volatile isn't read
或者像这样:
b = 1;
a = b; // volatile is read
b = 1;
a = 1; // volatile isn't read
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以生成赋值表达式的值
更新:
所以,这是一个两难的问题
如果“赋值后对象的值”不是从易失性对象的额外读取中获得的,则编译器假定易失性对象b
:
- 能够保存写入其中的任意
int
值,但它可能不是(例如,位0硬连线为0,这对于硬件寄存器来说并不罕见,我们应该使用volatiles)
- 无法在发生赋值写入的点和获得表达式值的点之间进行更改(同样,这可能是硬件寄存器的问题)
正因为如此,表达式值,如果不是从易失性对象的额外读取中获得的,则不会产生易失性对象的值,这是标准要求的情况
这两种假设似乎都不符合易失性对象的性质
如果,OTOH,“赋值后对象的值”是从所述易失性对象的额外隐含读取中获得的,则使用易失性左操作数计算赋值表达式的副作用取决于表达式值是否使用或是否完全任意,这将是一个奇数,意外且记录不良的行为。C11澄清这是未指定的
您可以找到C11的最终草稿。你现在引用的第二句话是指脚注111:
赋值运算符将值存储在由左操作数指定的对象中。赋值表达式具有赋值后的左操作数的值(111),但不是左值
脚注111说:
允许实现读取对象以确定值,但不需要读取,即使对象具有volatile限定类型
我希望对volatile的赋值不意味着在存储之后读取(赋值的值是否在其他地方使用并不重要)。这意味着,根据标准对volatile
的定义,将硬件寄存器映射到volatile变量(或指向volatile的指针)不会具有几乎所有人都依赖的语义。@MichaelBurr好的观点。很好!因此,基本上,我在问题中提出的那种代码应该避免,除非可以容忍特定于实现的行为。@AlexeyFrunze:我认为这是不可避免的。想象一个硬件地址,它读取上次写入的值的平方根。现在想象一个有N个周期的延迟来计算它。我认为标准无法合理地定义“赋值后左操作数的值”。它可能需要一次读取,但它无法规定写入后读取的速度。因此,避免特定于实现的行为的唯一方法是禁止读取。@SteveJessop读取和不读取仍然有区别。C99-这里应该更清楚。@Alexey:同意。当下一个标准澄清某些内容时,这意味着上一个标准不够清晰(在C11作者看来):-)@rob:你说这是C11中定义的实现。它是?只要看看你引用的文字,我会说它没有具体说明。区别在于实现是否必须记录一种方式,让程序员知道它将做什么。