2015-07-20 81 views
3

易失性写入易失性常量是否引入未定义的行为?如果我在写作时跌跌撞撞地怎么办?volatile变量的间接变化可以视为未定义的行为?

volatile const int x = 42; 
const volatile int *p = &x; 
*(volatile int *)p = 8; // Does this line introduce undefined behavior? 
*(int *)p = 16; // And what about this one? 

Compilable code

+0

相关到[如何在同一地址产生2个不同的值的变量?](http://stackoverflow.com/q/22656734/1708801) –

回答

8

这是不确定的行为(两个语句),你试图修改 “初始” const对象。从C11(N1570)6.7.3/P6 类型限定符(重点煤矿):

如果试图修改通过使用左值的与 常量限定类型定义的对象与非-const-qualified 类型,行为是未定义的。

为了完整性它可能是值得增加,即标准也说:

如果试图指通过使用左值的与具有 volatile限定类型定义的对象 非易失性限定类型,行为未定义。

因此后者语句,即:

*(int *)p = 16; 

是未定义第二短语,以及(这是一个 “双UB”)。

我相信C++的规则是一样的,但是并不拥有C++ 14的副本来确认。

+0

像我的回答说,修改一个'const'值是无效的 - 但是,它删除'volatile'无效 - 它只会让编译器潜在地缓存全局值。 –

+0

@MatsPetersson:希望我的回答现在更清楚。是的,你是对的,你可以放弃'volatile'限定词,这不是UB。 –

+0

而常量被声明为volatile的事实不影响这种说法吗? – Qwertiy

5

写入最初为const的变量是未定义的行为,因此您所有写入*p的示例均未定义。

删除volatile本身并不是未定义的。

但是,如果我们有像const volatile int *p = (const volatile int*)0x12340000; /* Address of hw register */这样的东西,那么删除volatile可能会导致硬件更新寄存器值,但是您的程序不会检测到它。 (例如,如果我们“忙于等待”while(*p & 0x01) ;,编译器应该每次都重新加载p指向的值,但while((*(const int *)p) & 1) ;,编译器完全可以自由读取值1,并且如果有一点再次使用初始值循环0被设定)。

你当然可以有 extern volatie int x;然后用 const volatile int *p = &x;在一些代码,并 x得到您的当前转换单元之外的一些其他的代码更新(例如另一个线程) - 在这种情况下删除或者 constvolatile是有效,但如上所述,您可能会“错过”更新的值,因为除非您调用函数,否则编译器不希望全局值在模块外更新。 [编辑,不,拿走volatile通过铸造该值也是被禁止的标准 - 但是将constvolatile添加到某个东西然后再删除它,如果它原来的对象没有它]。

需要Edti2:volatile来告诉编译器“即使您认为什么都不应该改变它,值可能随时改变”。发生这种情况,在一般情况下,在两种情况:即在软件的外面完全更新

  1. 硬件寄存器 - 例如用于串行端口,定时器计数器寄存器状态寄存器,或中断的中断控制器的状态,举几个例子 - 有成千上万的其他变体,但它们都是一样的想法:硬件改变了数值,与访问这些寄存器的软件没有直接关系。
  2. 变量由进程内的另一个线程(或在共享内存的情况下,由另一个进程)更新 - 再次,编译器将无法“看到”这样的变化。通常情况下,可以通过调用wait-loops等内部的函数来编写看起来能够正常工作的代码,编译器会重新载入非本地变量的值,但一段时间后编译器决定内联该函数调用,并且然后意识到代码没有更新该值,因此不会重新加载值 - >当您检查“更新”值并找到与编译器之前已加载的旧值相同的值时。也

注意volatile不是任何种类的线程/进程的正确性的保障 - 它只是保证编译器不会跳过读取或写入该变量。程序员仍然需要确保例如依赖于排序的多个值确实以正确的顺序更新,并且在高速缓存在处理单元之间不一致的系统上,通过软件使高速缓存一致[for例如,一个CPU和一个GPU可能不会使用连贯的内存更新,因此CPU写入直到CPU上的缓存已被刷新才会写入GPU - 不会将代码volatile应用于此代码将修复此问题]

+0

你是什么意思的线程?如果我将相同的代码放入另一个线程并等待完成,代码是否会正确?或者你的意思是这个过程? – Qwertiy

+1

你是什么意思删除。如果你的意思是指向那么它也是未定义的。 *如果尝试 是通过使用非易失性限定类型的左值 来引用通过易失性限定类型定义的对象,则行为未定义。* – this

+0

@This:Ok,已更新。 –