可以将volatile volatile的间接变化视为未定义的行为吗?

时间:2015-07-20 22:37:36

标签: c++ c volatile undefined-behavior

易失性写入易失性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?

Compilable code

2 个答案:

答案 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会被您当前翻译单元之外的其他一些代码更新(例如另一个线程) - 在这种情况下删除constvolatile是有效的,但如上所述,您可能会“错过”更新的值,因为编译器不希望全局值在您的外部更新模块,除非你调用函数。 [编辑,不,通过转换值volatile在标准中也被禁止 - 但是添加constvolatile是有效的对某些东西,然后再次删除它,如果它提到的原始对象没有它]。

Edti2:volatile需要告诉编译器“值可能随时改变,即使你认为什么都不应该改变它”。通常,这种情况会在两种情况下发生:

  1. 在软件外部更新的硬件寄存器 - 例如串行端口的状态寄存器,定时器计数器寄存器或中断控制器的中断状态,仅举几例 - 还有数千种其他变体,但它的想法完全相同:硬件改变了价值,与访问这些寄存器的软件没有直接关系。
  2. 变量由进程中的另一个线程更新(或者在共享内存的情况下,由另一个进程更新) - 再次,编译器将无法“看到”此类更改。通常,可以通过调用wait-loops等函数来编写看似正常运行的代码,并且编译器无论如何都会重新加载非局部变量的值,但是一段时间后编译器决定内联该函数调用,并且然后意识到代码没有更新该值,因此不会重新加载值 - >检查“已更新”值并找到编译器之前已加载的旧值时的错误。
  3. 另请注意,volatile不保证任何类型的线程/进程正确性 - 它只是保证编译器不会跳过对该变量的读取或写入。程序员仍然需要确保例如依赖于排序的多个值确实以正确的顺序更新,并且在高速缓存在处理单元之间不一致的系统上,通过软件使高速缓存变得一致[用于例如,CPU和GPU可能不会使用连贯的内存更新,因此在CPU上刷新缓存之前,CPU的写入不会到达GPU - 对代码应用volatile的数量不会解决这个问题]