大于本机尺寸类型的商店订购 - C 规范

Store ordering for larger-than-native-size type - C spec

提问人:Sparky McAnderson 提问时间:11/9/2023 最后编辑:Peter CordesSparky McAnderson 更新时间:11/9/2023 访问量:82

问:

假设你有一个指向大于本机类型整数的指针,最近的 C 规范对商店订购有什么看法?例如:

volatile uint64_t *test = (volatile uint64_t *)(addr);
*test = 0;

在 32 位架构上,这将编译为两个存储,C 规范是否说明哪个存储将是第一个(addr 或 addr+4)?还是定义了实现?

C 组件 嵌入式 易失性

评论

2赞 fuz 11/9/2023
这是一个复杂的话题,但 TL;DR:未定义对共享存储的并发读/写或写/写访问,而无需同步。如果需要,请使用 atomics(voltile 既不使变量原子化,也不意味着内存排序![除非在 MSVC 上])、栅栏、锁或其他同步设施。
2赞 fuz 11/9/2023
请注意,“大于原生尺寸”是一条红鲱鱼。不能保证对任何非原子类型的写入都发生在一个存储中,唯一的例外是写入 类型的变量,但对于该类型,该属性主要与信号处理程序有关。sig_atomic_t
3赞 Peter Cordes 11/9/2023
从 C 程序中的另一个线程观察它将是数据竞赛 UB。ISO C 标准没有说明易失性访问的内部细节,因此这完全取决于实现。(编译器在处理易失性内存位置时必须遵循哪些规则?如果您有两个相邻的 MMIO 寄存器并且顺序很重要,最好使用两个单独的访问。真正的实现可能确实有一个标准顺序,但它只是一个实现细节。volatile uint32_t
2赞 Peter Cordes 11/9/2023
@fuz:请注意,保证仅适用于;平原通常只是.一些编译器(如GCC和clang)如果可能的话,如果可能的话,将使用单个指令进行访问的请求(如SSE2),使得Linux内核中的手动原子与其他T的编译器一起使用(64位计算机上的哪些类型在gnu C和gnu C++中是天然原子的? - 意味着它们具有原子读取, 和原子写)但那当然不是volatile sig_atomic_tsig_atomic_tintvolatilevolatile
2赞 Andrew Henle 11/9/2023
C 规范是否说明了哪个商店将首先出现(addr 或 addr+4)?你根据什么假设它是两个存储,而不是一个字节,然后四个字节,然后三个字节?addraddr + 1addr + 5

答:

0赞 chux - Reinstate Monica #1

最近的 C 规范对商店订购有何看法?

字节序是实现定义的。

C 规范是否说明了哪个商店将首先出现(addr 或 addr+4)?

它是实现定义的。它可能是同时进行的。它甚至不需要保持一致。

评论

0赞 fuz 11/9/2023
这与内延性有什么关系?
0赞 chux - Reinstate Monica 11/9/2023
@fuz 使用 ,我们不知道 LS 32 位是在较低的地址还是 MS 32 位或其他地址中。正如 OP 询问的“商店排序”一样,这可能意味着保存的顺序和/或保存的字节序。在 OP 的案例中,两者都是实现定义的volatile uint64_t *test = (volatile uint64_t *)(addr);
1赞 fuz 11/9/2023
哦,我明白。我从未见过术语“endian(ess)”用于指代商店订单。
1赞 Clifford 11/12/2023
@fuz我认为这是最常见的情况。同时存在时间顺序和地址顺序的问题。第一个问题不清楚哪个意思,甚至第二个问题也是模棱两可的——首先是记忆还是时间?这可能无关紧要,因为 C 标准对此也无话可说。
3赞 Lundin 11/9/2023 #2

C 标准没有提到商店订单。它可能按寻址顺序发生,这意味着最低地址的数据可能会首先被复制(大端序上的 MS 字节,小端上的 LS 字节)。可能(但不能保证),因为按地址顺序复制在大多数 ISA 上最有意义,尤其是那些具有数据缓存的 ISA。


有点相关:

C 标准 (5.1.2.3) 所保证的是,整个 64 位将被复制到这里的两个序列点之间:

... ;      // the semi colon is a sequence point
*test = 0; // the semi colon is a sequence point

由于未使用限定符,因此无法保证原子性。因此,访问可能会被上下文切换到另一个线程或进程而中断 - 不能保证访问是线程安全的。_Atomic

5.1.2.3 不是标准中最好的部分,但如果你从字面上理解它,那么它还说指令重新排序不能发生在一个对象上,本质上使它充当记忆屏障。volatile

然而,另一个“语言律师”的方面是,自 C90 以来,C 标准一直被打破,说的是对象而不是左值访问。在这里的代码中,指向的数据不一定是对象,也许只有指针才是。因为编译器可能不知道您从中转换指针的地址上存储了什么,或者该对象获得了什么“有效类型”和限定符。这个语言“错误”将在即将到来的 C23 标准中得到修复,该标准定义并使用术语易失性访问volatilevolatilevolatile