假设我有以下代码:
volatile char array[4];
array[0] = 1;
Run Code Online (Sandbox Code Playgroud)
现在,理想情况下,编译器会将其转换为8位存储指令,以便在内存中只修改一个字节.但是,将它转换为读/修改/写可以自由吗?例如,处理此问题的〜等效方法如下:
int32 *temp = (int*)array;
*temp = (*temp & 0xFFFFFF00) | 1;
Run Code Online (Sandbox Code Playgroud)
问题显然是后一种实现将覆盖数组中的其他3个字节.在单线程应用程序中,这是等效的,但不是在多线程的情况下.
那么编译器允许编译第一个实现与第二个相同吗?
我认为答案是否定的; 它违反了'似乎'规则.由于重写涉及读取,因此代码与抽象模型的行为不同,特别是因为volatile
涉及,因此重写将无效.C11标准的第5.1.2.3节适用:
5.1.2.3程序执行
1本国际标准中的语义描述描述了抽象机器的行为,其中优化问题无关紧要.
2访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用,12)这些是执行环境状态的变化.表达式的评估通常包括值计算和副作用的开始.左值表达式的值计算包括确定指定对象的身份.
...
4在抽象机器中,所有表达式都按语义指定进行计算.实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用).
...
6对符合要求的实施的最低要求是:
- 根据抽象机器的规则严格评估对易失性对象的访问.
- 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序的结果相同.
- 交互设备的输入和输出动态应按照7.21.3的规定进行.这些要求的目的是尽快出现无缓冲或行缓冲输出,以确保在程序等待输入之前实际出现提示消息.
这是该程序的可观察行为.
12)用于二进制浮点运算的IEC 60559标准要求某些用户可访问的状态标志和控制模式.浮点运算隐式设置状态标志; 模式影响浮点运算的结果值.支持这种浮点状态的实现需要将其更改视为副作用 - 有关详细信息,请参见附录F. 浮点环境库
<fenv.h>
提供了一个编程工具,用于指示这些副作用何时起作用,在其他情况下释放实现.
C99中的措辞稍微简单一些,因为它不必考虑线程等,但措辞的要点是相同的.