13.6挥发性物体

关键字不稳定的在可移植代码中经常被误解。它的使用抑制了一些内存访问优化,但程序员经常希望它有一个不同于实际意义。

不稳定的是为访问以下特殊对象的代码而设计的内存映射设备寄存器,其内容会自动更改。这样的代码本质上是低级的,很难指定便携什么不稳定的在这些情况下是指。C标准说:“什么构成了对具有volatile-qualified类型是实现定义的,“所以在理论上每个实施应该通过记录什么来填补空白易挥发的实现的方法。但实际上,此文档通常不存在或不完整。

混淆的一个方面是用定义的对象之间的区别volatile类型和volatile左值。从C标准的角度来看视图中,用volatile类型定义的对象具有外部可视性行为。你可以把这些物体想象成只有一个小示波器将探测附加到它们上,以便用户可以观察访问它们,就像用户可以观察写入输出的数据一样文件夹。然而,该标准并未明确用户是否可以观察volatile左值对普通对象的访问。例如:

/*声明并访问易失性对象。用户可以“看到”对X的访问*/静态int volatile x;x=1;/*通过一个不稳定的左值访问两个普通对象。目前尚不清楚对*P的访问是否“可见”*/整数y;int*z=malloc(sizeof(int));int volatile*p;p=&y;*p=1;p=z;*p=1;

程序员经常希望这样不稳定的意思是“执行记忆此时此地访问,不合并多个内存访问,不更改内存字大小,而不重新排序。”但C标准不要求这样。对于使用volatile定义的对象类型,必须在下一个序列点之前进行访问;但是否则,允许合并、重新排序和更改字号。更糟糕的是,从标准来看,不稳定左值是否能提供更多如果基础资产物体是普通的。

即使在访问使用volatile类型定义的对象时,C标准只允许极其有限的信号处理程序:在C99中,如果信号处理程序读取任何非局部对象,或写入任何非本地对象其类型不是sig原子挥发性,或调用任何标准库函数,而不是中止,信号、和_退出。因此C编译器不需要担心信号处理程序干扰普通计算。C11和Posix允许一些额外的可移植信号处理程序中的行为,但仍有相当大的限制。

一些C实现允许在每个翻译单位,使实际行为与行为一致只有在调用其他函数时,标准才需要转换单元,并且信号处理程序的行为就像从不同的翻译单位。C99标准暗示实现,信号处理程序引用的对象“需要的显式规范不稳定的存储,以及其他实施定义的限制。”但不幸的是,即使这样特殊情况下,这些其他限制往往没有得到很好的记录。这一领域在C11中发生了重大变化,最终实现了可能会朝C11方向前进,但这需要一些时间。请参见何时访问易失性对象?在里面使用GNU编译器集合(GCC),对于一些GCC施加的限制。请参见定义信号处理程序在里面GNU C库,对于一些GNU C库施加的限制。限制在其他平台上有所不同。

如果可能的话,最好使用适合C和Posix标准规定的限制。

如果这不实用,您可以尝试以下经验法则。A类信号处理程序应该只访问可变左值,最好是左值引用使用volatile类型定义的对象,不应假设访问的对象具有内部一致的状态如果它们比机器单词大。此外,安装人员应该使用常用的编译器和编译器选项用于构建操作系统内核,因为内核通常需要更多不稳定的而安装程序在类似的环境中编译应用程序有时会受益内核对编译器施加的额外约束。诚然,我们在这里有些省吃俭用,因为这里很少该领域的担保;经验法则可能有助于修复一些错误但他们很有可能不会全部解决这些问题。

对于易挥发的,C++有与C相同的问题。多线程应用程序在不稳定的,但它们超出了本节的范围。

底线是使用不稳定的典型的伤害性能,但不应影响正确性。在某些情况下,它的使用确实有助于正确性,但这些情况往往被理解得很差这经常会增加不稳定的到数据结构缓解了错误的一些症状,而不是一般地修复错误。