5

假设我有以下代码:

volatile char array[4];
array[0] = 1;

现在,理想情况下,编译器会将其转换为 8 位存储指令,以便在内存中只修改一个字节。但是,是否可以免费将其转换为读/修改/写?例如,处理此问题的等效方法如下:

int32 *temp = (int*)array;
*temp = (*temp & 0xFFFFFF00) | 1;

问题显然是后一种实现将覆盖数组中的其他 3 个字节。在单线程应用程序中这是等价的,但在多线程情况下则不然。

那么编译器是否允许编译第一个实现与第二个实现相同?

4

2 回答 2

6

我认为答案是否定的;它违反了“好像”规则。由于重写涉及读取,代码的行为与抽象模型不同,尤其volatile是涉及到,因此重写将是无效的。C11 标准第 5.1.2.3 节适用:

5.1.2.3 程序执行

1 本国际标准中的语义描述描述了与优化问题无关的抽象机器的行为。

2 访问 volatile 对象、修改对象、修改文件或调用执行任何这些操作的函数都是副作用12)是执行环境状态的变化。表达式的评估通常包括值计算和副作用的启动。左值表达式的值计算包括确定指定对象的身份。

...

4 在抽象机中,所有表达式都按照语义的规定进行评估。如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。

...

6 对一致性实施的最低要求是:

  • 对 volatile 对象的访问严格按照抽象机的规则进行评估。
  • 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。
  • 交互设备的输入和输出动态应按照 7.21.3 中的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保在程序等待输入之前实际出现提示消息。

这是程序的可观察行为

12)二进制浮点运算的 IEC 60559 标准要求某些用户可访问的状态标志和控制模式。浮点运算隐式设置状态标志;模式影响浮点运算的结果值。支持这种浮点状态的实现需要将对其的更改视为副作用——详见附件 F。浮点环境库<fenv.h>提供了一种编程工具,用于指示这些副作用何时重要,在其他情况下释放实现。

C99 中的措辞稍微简单一些,因为它不必考虑线程等,但措辞的要点是相同的。

于 2013-07-16T19:34:56.297 回答
4

首先:使用volatile,在任何C 版本中肯定是不允许的——它被视为 I/O(如)。putc

否则...

我相信它对没有线程模型的 pre-C11 有效;这是一个顺序一致的转换。

我怀疑对于 C11 及更高版本,答案与 C++11 的线程模型相同,这是肯定的,因为线程模型是 SC-DRF:顺序一致,无数据竞争

看一下这个视频 (12:36) -- Herb Sutter 提到了这个确切的情况,对于一个有效的 SC-DRF 编译器来说答案是否定的;符合 C++11 的编译器绝不能发明对不会被写入的变量的写入

请注意,这volatile不是必须的。

如果存在编译器错误,那就另当别论了。

于 2013-07-16T19:31:39.560 回答