考虑以下代码示例:
int main(void)
{
volatile int a;
static volatile int b;
volatile int c;
c = 20;
static volatile int d;
d = 30;
volatile int e = 40;
static volatile int f = 50;
return 0;
}
如果没有volatile
,编译器可以优化掉所有变量,因为它们永远不会被读取。
我认为a
和b
可以被优化掉,因为它们完全未被使用,请参阅unused volatile variable。
我认为c
和d
因为被写入而无法删除,并且必须实际发生对volatile变量的写入。 e
应相当于c
。
GCC不会优化远离f
,但它也不会发出任何写入指令。在数据部分中设置50。 LLVM(clang)完全删除f
。
这些陈述是真的吗?
答案 0 :(得分:9)
写入volatile变量(甚至是自动变量)计为可观察行为。
C11(N1570)5.1.2.3/6:
符合实施的最低要求是:
- 严格按照摘要规则评估对易失性对象的访问 机。
- 程序终止时,写入文件的所有数据应与结果相同 根据抽象语义执行程序会产生。
- 交互设备的输入和输出动态应按照规定进行 7.21.3。这些要求的目的是无缓冲或行缓冲输出 尽快出现,以确保提示消息实际出现在之前 等待输入的程序。
这是该计划的可观察行为。
问题是:初始化(e
,f
)是否计入"访问"?正如Sander de Dycker所指出的,6.7.3说:
对具有volatile限定类型的对象的访问构成是实现定义的。
这意味着它可以由编译器决定是否可以优化e
和f
- 但这必须记录在案!
答案 1 :(得分:4)
严格地说,根据C标准,无法优化访问(读取或写入)的任何易失性变量。标准表示对volatile对象的访问可能具有未知的副作用,并且对volatile对象的访问必须遵循C抽象机器的规则(其中所有表达式都按其语义指定进行评估)。
从强大的标准(强调我的):
(C11,6.7.3p7)"具有volatile限定类型的对象可能会以未知的方式进行修改 实施或有其他未知的副作用。因此任何表达引用 对这样的对象应严格按照抽象机的规则进行评估, 如5.1.2.3。"
中所述
接下来,即使简单的变量初始化也应被视为访问。请记住,static
说明符也会导致对象被初始化(到0
)并因此被访问。
现在已知编译器对volatile限定符的行为有所不同,我猜他们中的很多只会优化示例程序的大多数volatile对象,除了那些具有显式赋值(=
)的对象。