编译器是否需要将存储发送到原始地址?

Is the compiler required to emit stores to raw addresses?

两个流行的编译器(gcc、clang)在以下函数的主体中发出一条存储指令:

void foo(char x) {
  *(char *)0xE0000000 = x;
}

此程序可能在某些硬件架构上运行正确,其中写入的地址是内存映射 IO。

由于此访问是通过不符合 volatile 资格的指针执行的,因此编译器是否需要在此处发出存储?一个足够激进的优化器能否合法地消除这个存储?我很好奇这家商店是否构成了抽象机器的可观察到的副作用。

此外,C17 和 C++20 在这方面有什么不同吗?

C 标准不需要实现来发布存储,因为 C 2018 5.1.2.3 6 说:

The least requirements on a conforming implementation are:

— Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.

— At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced.

— The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input.

This is the observable behavior of the program.

None 其中包括对非易失性左值的赋值。

在 C 中,明确允许将整数转换为任何指针类型,但这并不意味着您可以使用生成的指针来访问内存。除非转换出现在空指针常量(不能用于访问内存)的上下文中,否则转换的结果

is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

(C17, 6.3.2.3/5)

“实现定义”为后续访问假定指向的对象留下了丰富的范围,使其具有不涉及访问内存的行为,远远超出执行陷阱或展示未对齐访问的(未指定)影响或甚至因为严格的别名违规而表现出 UB 礼貌。

只有克服所有这些问题,您才能解决@EricPostpischil 在 .

中提出的围绕非 volatile 访问的问题

底线是,如果您正在为一个独立的实现编写代码,这是唯一可以尝试执行示例代码中的访问完全有意义的上下文,那么您需要查阅关于如何访问绝对地址的实施文档。

据我所知,同样的结论也适用于 C++。