5.1.2.3定义了以下内容:

在抽象计算机中,所有表达式都按照 语义学.实际的实现不需要计算 表达式,如果它可以推导出它的值不被使用并且没有 产生所需的副作用(包括调用 函数或通过对对象的易失性访问).

对我来说,这个定义听起来有点模糊.我特别关心Sequenced before的定义,以及它如何(如果)与volatile挂钩.

以下面的函数为例:

void foo(volatile int* a, int *b, volatile int *c){
    *a = 1;
    *b = 2;
    *c = 3;
}

基本上我有两个问题:

  1. *a = 1;是在*c = 3之前排序的吗?
  2. *a = 1;是在*b = 2;之前排序的吗?

如果是,原因何在?

5.1.2.3之前排序的定义为:

按顺序排列的是不对称的、可传递的、成对的关系 在由单个线程执行的求值之间,这会导致 这些评价中的偏序

推荐答案

在定义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的行为略有变化.volatileaccess现在被视为volatileobject access,这意味着严格来说,像*(volatile int*)0x1234这样的东西并不遵循与volatile int* ptr = (volatile int*)0x1234; ... *ptr相同的规则.这是一个缺陷,有一个缺陷报告,它已经在C23中修复.

C++相关问答推荐

%p与char* 等组合缺少的GCC Wform警告

使用SWI—Prolog的qsave_program生成二进制文件有什么好处?'

Ebpf内核代码:permission denied:invalid access to map value

使用GOTO从多个嵌套循环C继续

是否可以使用指针算法在不对齐的情况下在 struct 中相同类型的字段的连续序列之间移动?

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

在C语言中,是否可以使枚举数向后计数?

预先分配虚拟地址空间的区域

GCC创建应用于移动项的单独位掩码的目的是什么?

对于C中给定数组中的每个查询,如何正确编码以输出给定索引范围(1到N)中所有数字的总和?

S将C语言宏定义为自身的目的是什么?(在glibc标题中看到)

为什么函数是按照定义的顺序执行的,而不是按照从avr-c中的int main()调用的顺序执行的?

我在反转双向链表时遇到问题

C将数组传递给函数以修改数组

GCC错误,共享内存未定义引用?

C:面筋蛋白';为什么不刷新窗口?

使用 c 中的 write() 函数将非 ASCII 字符写入标准输出

System V 消息队列由于某种原因定期重置

尽管将其标记为易失性,但 gcc 是否优化了我的等待代码?

在带中断的循环缓冲区中使用 易失性