为什么挥发性存在?

时间:2020-03-05 18:56:01  来源:igfitidea点击:

" volatile"关键字有什么作用?在C ++中,它可以解决什么问题?

就我而言,我从来没有故意需要它。

解决方案

回答

开发嵌入式系统或者设备驱动程序时需要"易失性",在其中我们需要读取或者写入内存映射的硬件设备。特定设备寄存器的内容可以随时更改,因此我们需要使用" volatile"关键字来确保编译器不会对此类访问进行优化。

回答

如果要从内存中的某个点读取数据,例如一个完全独立的进程/设备/可能写入的内容,则需要" volatile"。

我曾经在直接C语言的多处理器系统中使用双端口ram。我们使用硬件管理的16位值作为信号量来了解其他人何时完成。本质上,我们这样做:

void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

没有`volatile',优化器会认为循环是无用的(那家伙从来没有设置值!他真是疯了,摆脱了那个代码!),而我的代码将在没有获得信号量的情况下继续进行,从而导致以后出现问题。

回答

  • 我们必须使用它来实现自旋锁以及某些(全部?)无锁数据结构
  • 与原子操作/指令一起使用
  • 一次帮助我克服了编译器的错误(优化过程中错误生成的代码)

回答

为嵌入式开发,我有一个循环,用于检查可以在中断处理程序中更改的变量。如果没有" volatile",就编译器所知,该循环将变为noop,该变量永不更改,因此可以优化检查。

同样的情况也适用于在更传统的环境中可以在不同线程中更改的变量,但是我们经常进行同步调用,因此编译器在优化方面并不是那么自由。

回答

从Dan Saks的嵌入式系统文章中:

"易失性对象是其值可能自发改变的对象。也就是说,当我们声明一个对象为易失性对象时,我们是在告诉编译器该对象可能会更改状态,即使程序中没有任何语句显示要更改它。

链接到Saks先生有关volatile关键字的2篇很棒的文章:

http://www.embedded.com/columns/programmingpointers/174300478
http://www.embedded.com/columns/programmingpointers/175801310

回答

大多数现代处理器的浮点寄存器的精度都超过64位。这样,如果我们对双精度数字运行多个运算,则实际上得到的精度要比将每个中间结果截断为64位的精度更高。

通常这很好,但这意味着根据编译器如何分配寄存器和进行优化,对于完全相同的输入,完全相同的操作将有不同的结果。如果需要一致性,则可以使用volatile关键字强制每个操作返回到内存。

对于一些没有代数意义但减少浮点误差的算法(例如Kahan求和),它也很有用。从代数角度讲,这是一个小问题,因此除非某些中间变量是可变的,否则它经常会被错误地优化掉。

回答

除了使用volatile关键字告诉编译器不要优化对某些变量的访问(可以通过线程或者中断例程进行修改)外,还可以使用它来消除一些编译器错误-是的,可以---。

例如,我在嵌入式平台上工作,当时编译器对变量的值做出了错误的假设。如果未对代码进行优化,则程序可以正常运行。使用优化(因为这是关键例程,确实需要这样做),代码将无法正常工作。唯一的解决方案(尽管不是很正确)是将'faulty'变量声明为volatile。

回答

我曾经在1990年代初期工作的一个大型应用程序包含使用setjmp和longjmp的基于C的异常处理。 volatile关键字对于变量的值是必需的,这些变量的值需要保留在用作" catch"子句的代码块中,以免这些var存储在寄存器中并被longjmp清除。

回答

当编译器坚持优化掉我希望在执行代码时能够看到的变量时,我已经在调试版本中使用了它。

回答

除了按预期方式使用它外,volatile还用于(模板)元编程中。它可以用于防止意外重载,因为volatile属性(如const)参与了重载解析。

template <typename T> 
class Foo {
  std::enable_if_t<sizeof(T)==4, void> f(T& t) 
  { std::cout << 1 << t; }
  void f(T volatile& t) 
  { std::cout << 2 << const_cast<T&>(t); }

  void bar() { T t; f(t); }
};

这是合法的;这两个重载都可能是可调用的,并且几乎相同。因为我们知道bar无论如何不会通过非易失性T,所以volatile强制转换是合法的。但是,volatile版本严格来说更糟,因此,如果提供了非易失性f,请不要在过载解析中选择。

注意,代码实际上从不依赖于" volatile"存储器访问。

回答

实现无锁数据结构时,必须使用volatile。否则,编译器可以自由优化对变量的访问,这将改变语义。

换句话说,volatile告诉编译器对该变量的访问必须与物理内存的读/写操作相对应。

例如,这是在Win32 API中声明InterlockedIncrement的方式:

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);