c++ - 在 C 中递增一个 volatile 变量

标签 c++ c language-lawyer volatile

考虑以下三个表达式:

++x;
x += 1;
x = x + 1;

据我所知,它们在语义上是相同的,忽略了 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;
}

在所有这四个平台上,这三个函数产生相同的汇编程序输出,无论是在 -O1 还是 -O3。在 AMD64 上,这只是两条指令:

incq    _x(%rip)
retq

因此, 这个说法背后有什么道理吗?如果是这样,有什么区别,我该如何公开它?

注意:我完全清楚 volatile 不能保证原子性。这不是我在这里要问的 - 除非原子性本身是三者之间的不同之处。

最佳答案

来自 C++ 标准草案 5.3.2 [expr.pre.incr] 说:

If x is not of type bool, the expression ++x is equivalent to x+=1

5.17 [expr.ass] 说:

The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.

所以 ++xx += 1 是等价的。

现在 x += 1x = 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;
}

在这种情况下,与这种情况相反,x 将被计算两次:

void b(void)
{
   y[x] += 1;
}

和一个godbolt session shows对于 b():

b():                                  # @b()
movq    x(%rip), %rax
incq    y(,%rax,8)
retq

对于c():

c():                                  # @c()
movq    x(%rip), %rax
movq    y(,%rax,8), %rax
incq    %rax
movq    x(%rip), %rcx
movq    %rax, y(,%rcx,8)
retq

据我所知,这也适用于 C11。来自 C11 部分 6.5.3.1 前缀递增和递减运算符:

The expression ++E is equivalent to (E+=1).

来自 6.5.16.2 部分的复合赋值:

Acompound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once

关于c++ - 在 C 中递增一个 volatile 变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32722789/

相关文章:

c++ - 在非事件联盟成员上使用 `std::addressof` 是否明确

c++ - 了解使用C++赋值时的运算符 “less”或 “greater”

c++ - 使用 C++ 和 glm 转换位置数组

c - Windows 锁定时截取桌面屏幕截图 (Win+L)

c - 实现和测试 thread_create 函数

c - 这个声明在 C11 标准中意味着什么(关于可变参数函数)?

C++ free() 改变其他内存

c++ - 对象数组的插入方法c++

计算用户n值的e值?

c - C 中的胖指针