所有全局变量都应该是波动合格的吗?

spr*_*aff 11 c c++ global-variables qualifiers volatile

在这个例子中,是否需要global_value声明正确性volatile

int global_value = 0;

void foo () {
    ++ global_value;
}

void bar () {
    some_function (++global_value);
    foo ();
    some_function (++global_value);
}
Run Code Online (Sandbox Code Playgroud)

我的理解是volatile" 指向 "用于指向映射内存和变量的指针,这些指针可以通过信号修改(并且强调不是为了线程安全)但是很容易想象bar可能编译成这样的东西:

push EAX
mov EAX, global_value
inc EAX
push EAX
call some_function
call foo
inc EAX
push EAX
call some_function
mov global_value, EAX
pop EAX
Run Code Online (Sandbox Code Playgroud)

这显然是不正确的,但即使没有volatile我认为根据C抽象机器它是有效的.我错了还是有效?

如果是这样的话,在我看来volatile经常被忽视.这不是什么新鲜事!


扩展示例

void baz (int* i) {
    some_function (++*i);
    foo ();
    some_function (++*i);
}

int main () {
    baz (&global_value);
}
Run Code Online (Sandbox Code Playgroud)

即使bar保证编译成正确的dont-cache-global_value实现,也baz同样正确,还是允许缓存非易失性值*i

Rol*_*lig 10

不,volatile这里不需要关键字.由于global_value在函数外部是可见的bar,因此如果调用另一个函数,编译器不能假定它保持不变.

[更新2011-07-28]我发现了一个很好的引用,证明了这一切.它是在ISO C99,5.1.2.3p2中,我太懒了,不能完全复制到这里.它说:

在称为序列点的执行序列中的某些特定点处,先前评估的所有副作用应该是完整的,并且不会发生后续评估的副作用.

序列点包括:

  • 在评估参数之后调用函数(6.5.2.2).
  • 完整表达式的结束:[...]表达式语句中的表达式(6.8.3); [...]

你有证明.

  • 你或许可以引用从`foo`返回的序列点这一事实.因此,`foo`中的代码必须在此之前编写对象.调用`foo`之后`main`中的代码必须使用在该序列点建立的`global_value`的实际值,而不是某些不同的值.很难说它在标准中的哪个位置,"当你说'global_value`时,这意味着'global_value`,而不是它的某些先前值,或者就此而言,其他一些数字已经凭空消失了".正如Roland所说,这在单线程代码中很明显,这是所有标准地址. (2认同)

R..*_*R.. 5

volatile涉及longjmp,信号处理程序,内存映射设备驱动程序以及编写自己的低级多线程同步原语的唯一用途.然而,对于最后一次使用volatile是不够的,甚至可能不是必需的.你肯定还需要asm(或编译器特定的或C1x原子)来进行同步.

volatile 对于任何其他目的都没有用,包括您询问的代码.