以下代码示例来自Chinese blog,其中介绍了volatile
的效果。左边是C代码;另一个是生成的汇编代码。
// cordering.c gcc -O2 -S -masm=intel cordering.c
int A;
volatile int B;
void foo() mov eax, DWORD PTR B[rip]
{ mov DWORD PTR B[rip], 0
A = B + 1; add eax, 1
B = 0; mov DWORD PTR A[rip], eax
} ret
正如我们在汇编代码中看到的那样,A
的副作用放在B
的副作用之后,即使B
符合volatile
。但是,cppreference.com says:
[W]在单个执行线程中,无法优化出易失性访问,或者重新排序另一个可见的副作用,即在易失性访问之后排序或排序 - 。
这里A
的副作用在B
之前排序,所以我认为编译器执行此操作是非法的。我是对的吗?
作为补充,博客说如果我们想要保证volatile
和non-volatile
类型之间的顺序,我们需要同时制作volatile
:
// cordering.c gcc -O2 -S -masm=intel cordering.c
volatile int A;
volatile int B;
void foo() mov eax, DWORD PTR B[rip]
{ add eax, 1
A = B + 1; mov DWORD PTR A[rip], eax
B = 0; mov DWORD PTR B[rip], 0
} ret
答案 0 :(得分:5)
您关联的页面显示:
每次访问(读取或写入操作,成员函数调用等) 通过glvalue表达的volatile属性类型是 为优化目的而视为可见的副作用 (即,在单个执行线程内,易失性访问 无法优化或重新排序另一个可见的副作用 在易失性访问之前进行排序或排序。
因此,如果A
不是volatile
,则对A
的访问不会被视为可见的副作用,而第二个语句也不适用(因为它没有#&# 39;关于重新排序可见和不可见访问的任何内容。)
编辑: 请注意,cppreference不是C ++的官方文档,而是社区工作。我确实认为粗体语句的意图是定义可见副作用是什么,但它没有写清楚。
最终引用是C ++标准(here是获取它的一些选项)。 N4659标准草稿,在 [intro.execution] ,第14段和可见副作用中定义副作用 [intro.races] 中的2页定义链。 我不是C ++标准的专家,因此我无法在没有付出巨大努力的情况下破译标准所说的内容,但欢迎您试一试。
但是,有关允许编译器执行哪些优化的非正式解释,您可以查看cppreference上的as-if rule。
编辑2:该标准还正式指定 [intro.execution] 中的 as-if规则,第7段:
符合实施的最低要求是:
(7.1) - 通过不稳定的glvalues访问是严格按照 抽象机的规则。
(7.2) - 在程序终止时,全部 写入文件的数据应与可能的数据相同 结果是根据摘要执行程序 语义会产生。
(7.3) - 输入和输出动态 交互设备应以这样的方式进行 提示输出实际上是在程序等待之前传递的 输入。构成交互设备的是什么 实现定义的。
简而言之,任何优化都是有效的,只要程序生成相同的输出,并且对volatile对象的读写操作顺序正确,这适用于您的原始示例。