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

Qwe*_*tiy 3 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?
Run Code Online (Sandbox Code Playgroud)

可编译的代码

Grz*_*ski 8

当您尝试修改"初始" const对象时,它是未定义的行为(对于两个语句).从C11(N1570)6.7.3/p6 类型限定符(强调我的):

如果尝试通过使用具有非const限定类型的左值来修改使用const限定类型定义的对象,则行为是未定义的.

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

如果尝试通过使用具有非volatile限定类型的左值来引用使用volatile限定类型定义的对象 ,则行为未定义.

因此后面的陈述,即:

*(int *)p = 16;
Run Code Online (Sandbox Code Playgroud)

对于第二个短语也是未定义的(它是"双UB").

我相信C++的规则是相同的,但是没有C++ 14的副本来确认.


Mat*_*son 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等函数来编写看似正常运行的代码,并且编译器无论如何都会重新加载非局部变量的值,但是一段时间后编译器决定内联该函数调用,并且然后意识到代码没有更新该值,因此当您检查"updated"值并找到编译器之前已加载的相同旧值时,不会重新加载值 - > bug.

另请注意,这volatile并不能保证任何类型的线程/进程正确性 - 它只是保证编译器不会跳过对该变量的读取或写入.程序员仍然需要确保例如依赖于排序的多个值确实以正确的顺序更新,并且在高速缓存在处理单元之间不一致的系统上,通过软件使高速缓存变得一致[用于例如,CPU和GPU可能不会使用连贯的内存更新,因此在CPU上刷新缓存之前,CPU的写入不会到达GPU - 没有适用volatile于代码的数量将解决此问题]