在定义C17 5.1.2.3§3的相同部分中,再往下按顺序排列:
表达式A和B的求值之间的序列点的存在意味着与A相关联的每个值计算和副作用在与B相关联的每个值计算和副作用之前被排序.
这是非常清楚的,没有留下任何解释的余地.
在您的代码中,每;
个标记一个序列点(完整表达式的结尾).因此,毫无疑问,它是从上到下排序的,这与volatile
无关.
另一个问题是,是否允许优化编译器对表达式重新排序."抽象机器"的C语言概念含糊其辞,通常无济于事,但它说的是:
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual
implementation need not evaluate part of an expression if it can deduce that its value is not used and
that no needed side effects are produced (including any caused by calling a function or accessing a
volatile object).
/--/
Accesses to volatile
objects are evaluated strictly according to the rules of the abstract machine.
"语义"和"抽象机器的规则"指的是(除其他事项外)前面提到的"排序在前"部分.
除了禁止volatile
对象访问的指令重新排序之外,这不能很好地解释为任何其他方式.
然而,*b=2;
表达式不是volatile
限定的.这就是事情变得泥泞的地方.可以将上面引用的部分理解为"不允许跨volatile
访问的指令重新排序".这就是说,volatile
访问必须充当记忆屏障--我认为这是正确的解释.
但碰巧的是,在多核中使用预取和/或流水线实现并发执行的各种CPU制造商在设计过程中并不一定深入挖掘C标准.因此,硬件可能会规定如何进行重新排序,然后编译器供应商可以做很多事情来修复它.这样的硬件和该硬件端口的编译器可能是C语言的非一致性实现.
我们还可能注意到,在即将到来的C23中,volatile
的行为略有变化.volatile
access现在被视为volatile
object access,这意味着严格来说,像*(volatile int*)0x1234
这样的东西并不遵循与volatile int* ptr = (volatile int*)0x1234; ... *ptr
相同的规则.这是一个缺陷,有一个缺陷报告,它已经在C23中修复.