Chr*_*tix 11 c c++ volatile language-lawyer
考虑以下三个表达式:
++x;
x += 1;
x = x + 1;
Run Code Online (Sandbox Code Playgroud)
据我所知,它们在语义上是相同的,忽略了C++中的运算符重载.然而,今天我读到了一个断言,它们是不同的,特别x是宣布时volatile.
为了测试这个断言,我写了以下内容并为PowerPC,AMD64,ARMv6和68k编译了它:
#include <stdint.h>
static volatile uint64_t x = 0;
void a(void)
{
++x;
}
void b(void)
{
x += 1;
}
void c(void)
{
x = x + 1;
}
Run Code Online (Sandbox Code Playgroud)
在所有这四个平台上,这三个函数产生相同的汇编输出,无论是在-O1还是-O3.在AMD64上,这只是两条指令:
incq _x(%rip)
retq
Run Code Online (Sandbox Code Playgroud)
因此,是有这个说法背后的真相任何?如果是这样,有什么区别,我该如何揭露它?
注意:我完全清楚这volatile不保证原子性.这不是我在这里要求的 - 除非原子性本身在三者之间有所不同.
Sha*_*our 11
从草案C++标准部分5.3.2 [expr.pre.incr]说:
如果x不是bool类型,则表达式++ x等效于x + = 1
和5.17 [expr.ass]说:
形式E1 op = E2的表达式的行为等同于E1 = E1 op E2,除了E1仅被评估一次.
所以++x并且x += 1是等价的.
现在,其中一个案件x += 1从不同x = x + 1的是,E1只计算一次.在这种特殊情况下,它并不重要,但我们可以提出一个案例:
#include <stdint.h>
volatile uint64_t x = 0;
volatile uint64_t y[2] = {0} ;
void c(void)
{
y[x] = y[x] + 1;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,x将评估两次,而不是这种情况:
void b(void)
{
y[x] += 1;
}
Run Code Online (Sandbox Code Playgroud)
和godbolt会话显示为b():
b(): # @b()
movq x(%rip), %rax
incq y(,%rax,8)
retq
Run Code Online (Sandbox Code Playgroud)
并为c():
c(): # @c()
movq x(%rip), %rax
movq y(,%rax,8), %rax
incq %rax
movq x(%rip), %rcx
movq %rax, y(,%rcx,8)
retq
Run Code Online (Sandbox Code Playgroud)
据我所知,这也适用于C11.从C11部分6.5.3.1前缀增量和减量运算符:
表达式++ E等价于(E + = 1).
从6.5.16.2复合分配部分:
形式E1 op = E2的复合赋值等效于简单赋值表达式E1 = E1 op(E2),除了左值E1仅被评估一次
| 归档时间: |
|
| 查看次数: |
805 次 |
| 最近记录: |