易失性写入易失性常量是否引入未定义的行为?如果我在写作时跌跌撞撞地怎么办?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?
易失性写入易失性常量是否引入未定义的行为?如果我在写作时跌跌撞撞地怎么办?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?
这是不确定的行为(两个语句),你试图修改 “初始” const
对象。从C11(N1570)6.7.3/P6 类型限定符(重点煤矿):
如果试图修改通过使用左值的与 常量限定类型定义的对象与非-const-qualified 类型,行为是未定义的。
为了完整性它可能是值得增加,即标准也说:
如果试图指通过使用左值的与具有 volatile限定类型定义的对象 非易失性限定类型,行为未定义。
因此后者语句,即:
*(int *)p = 16;
是未定义第二短语,以及(这是一个 “双UB”)。
我相信C++的规则是一样的,但是并不拥有C++ 14的副本来确认。
像我的回答说,修改一个'const'值是无效的 - 但是,它删除'volatile'无效 - 它只会让编译器潜在地缓存全局值。 –
@MatsPetersson:希望我的回答现在更清楚。是的,你是对的,你可以放弃'volatile'限定词,这不是UB。 –
而常量被声明为volatile的事实不影响这种说法吗? – Qwertiy
写入最初为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
得到您的当前转换单元之外的一些其他的代码更新(例如另一个线程) - 在这种情况下删除或者
const
或
volatile
是有效,但如上所述,您可能会“错过”更新的值,因为除非您调用函数,否则编译器不希望全局值在模块外更新。
volatile
通过铸造该值也是被禁止的标准 - 但是将const
或volatile
添加到某个东西然后再删除它,如果它原来的对象没有它]。
需要Edti2:volatile
来告诉编译器“即使您认为什么都不应该改变它,值可能随时改变”。发生这种情况,在一般情况下,在两种情况:即在软件的外面完全更新
注意volatile
不是任何种类的线程/进程的正确性的保障 - 它只是保证编译器不会跳过读取或写入该变量。程序员仍然需要确保例如依赖于排序的多个值确实以正确的顺序更新,并且在高速缓存在处理单元之间不一致的系统上,通过软件使高速缓存一致[for例如,一个CPU和一个GPU可能不会使用连贯的内存更新,因此CPU写入直到CPU上的缓存已被刷新才会写入GPU - 不会将代码volatile
应用于此代码将修复此问题]
相关到[如何在同一地址产生2个不同的值的变量?](http://stackoverflow.com/q/22656734/1708801) –