易失性写入易失性const是否会引入未定义的行为?如果我在写作时丢弃易失性怎么办?
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?
答案 0 :(得分:8)
当您尝试修改“初始”const
对象时,它是未定义的行为(对于两个语句)。来自C11(N1570)6.7.3 / p6 类型限定符(强调我的):
如果尝试修改用a定义的对象 通过使用带有非const限定的左值的const限定类型 类型,行为未定义。
为了完整性,可能值得添加,标准也说:
如果尝试引用用a定义的对象 通过使用左值的波动限定类型 非易失性限定类型,行为未定义。
因此后面的陈述,即:
*(int *)p = 16;
对于第二个短语也是未定义的(它是“双UB”)。
我认为C ++的规则是相同的,但是没有C ++ 14的副本来确认。
答案 1 :(得分: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
会被您当前翻译单元之外的其他一些代码更新(例如另一个线程) - 在这种情况下删除const
或volatile
是有效的,但如上所述,您可能会“错过”更新的值,因为编译器不希望全局值在您的外部更新模块,除非你调用函数。volatile
在标准中也被禁止 - 但是添加const
或volatile
是有效的对某些东西,然后再次删除它,如果它提到的原始对象没有它]。
Edti2:volatile
需要告诉编译器“值可能随时改变,即使你认为什么都不应该改变它”。通常,这种情况会在两种情况下发生:
另请注意,volatile
不保证任何类型的线程/进程正确性 - 它只是保证编译器不会跳过对该变量的读取或写入。程序员仍然需要确保例如依赖于排序的多个值确实以正确的顺序更新,并且在高速缓存在处理单元之间不一致的系统上,通过软件使高速缓存变得一致[用于例如,CPU和GPU可能不会使用连贯的内存更新,因此在CPU上刷新缓存之前,CPU的写入不会到达GPU - 对代码应用volatile
的数量不会解决这个问题]