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

Can indirect change of volatile const be treated as undefined behavior?

本文关键字:未定义 const 变化 volatile      更新时间:2023-10-16

volatile写入volatile const是否会引入未定义行为?如果我在写的时候丢掉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代码

当您试图修改"initial" const对象时,这是未定义的行为(对于两个语句)。来自C11 (N1570) 6.7.3/p6 类型限定符(强调我的):

如果试图修改用通过使用带有非const限定类型的左值类型,行为未定义。

为完整起见,值得补充的是,标准还规定:

如果试图引用用通过使用带有的左值来限定volatile类型非易失性限定类型,行为未定义

因此后一个语句,即:

*(int *)p = 16;

对于第二个短语也是未定义的(它是一个"double UB")。

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

写入原来是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. 变量由进程内的另一个线程更新(或者在共享内存的情况下,由另一个进程更新)——同样,编译器将无法"看到"这样的更改。通常,一个人可以通过在等待循环中调用一个函数来编写看起来正常的代码,编译器会重新加载非局部变量的值,但过了一段时间后,编译器决定内联该函数调用,然后意识到代码没有更新该值,所以当你检查"更新"的值并找到编译器已经加载的相同的旧值时,不会重新加载value ->错误。

还需要注意的是,volatile并不能保证任何类型的线程/进程的正确性——它只是保证编译器不会跳过对该变量的读写操作。它仍然是程序员来确保例如多个值依赖于命令的确更新以正确的顺序,和在系统缓存处理单元之间不一致的,通过软件的缓存是连贯的(例如,CPU和GPU不得使用一致的内存更新,所以写的CPU不到GPU,直到缓存刷新在CPU——再多的应用volatile代码将解决这个问题)

相关文章: