繁体   English   中英

在C中增加volatile变量

[英]Incrementing a volatile variable in C

考虑以下三个表达式:

++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]中说:

如果x不是bool类型,则表达式++ x等效于x + = 1

5.17 [expr.ass]说:

E1 op = E2形式的表达式的行为等同于E1 = E1 op E2,除了E1仅被评估一次。

所以++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;
}

并为b() 显示一个godbolt会话

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前缀增量和减量运算符:

表达式++ E等价于(E + = 1)。

并从第6.5.16.2节复合指配:

形式E1 op = E2的复合赋值等效于简单赋值表达式E1 = E1 op(E2),除了左值E1仅被评估一次

在抽象语义中,所有这三个表达式都完全相同。 他们访问 x以检索其值,计算新值,然后将更新后的值存储回x 有一个访问和商店。 (表达式也会产生一个被丢弃的值)。

虽然x = x + 1提到x两次,但不评估左侧x 也就是说,并非完全:它的不计算。 仅根据确定指定值的位置来评估它。

因此,这里可能会对位置进行双重评估:左侧确定x的位置,右侧也是如此。 但确定位置不涉及访问位置本身。

对于某些类型的表达式,确定位置确实涉及访问值。 例如:

a[i] = a[i] + 1;

这是完全不同的

i = i + 1

因为i只是一个辅助变量,这里必须知道其值才能确定a[i]的存储位置(而且i本身甚至不会递增)。 如果ivolatile ,则在a[i] = a[i] + 1对它的两次抽象访问必须对应于两次实际访问。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM