函数调用之间是否刷新了全局变量?

时间:2013-02-01 01:20:59

标签: c gcc embedded c99 volatile

我正在编写嵌入式固件,并且发现有时很难确定何时需要使用volatile。

当我有一个等待某个布尔标志被中断改变的函数时,很明显该标志需要是易失性的,因为该函数会永远等待,因为编译器没有意识到该值可以是被中断改变了。

但是当我有一个只检查第一行中的标志的短函数时,我希望标志不需要是volatile,因为每次进入函数时它的值都会被读取?所以当一个中断在我第一次调用函数和第二次调用函数之间修改它的值时,我将得到新值。或者是否无法保证每次进入该功能时都会清除所有缓存寄存器?

5 个答案:

答案 0 :(得分:8)

你仍然需要标记你的变量volatile:因为优化器可以自由地内联你的函数,特别是短函数,在没有{{1}的循环中调用你的函数用于访问硬件修改内存的标记会使您在初始迭代后无法读取内存。

答案 1 :(得分:5)

  

...因为每次进入函数时都会读取它的值?

不,不能保证这一点。 “缺乏易失性错误”的问题在于编译器的优化器,不知道某个变量可以从外部源改变,改变了代码的整个含义。

所以如果你有这个:

static int x=0;

int func (void)
{
  if(x == 0)
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

interrupt void isr (void)
{
  x = SOMETHING;
}

然后编译器会思考:“嗯,x永远不会被修改,因为”isr“永远不会从程序中调用。所以x总是0,我会优化代码:”

int func (void)
{
  return 1;
}

然后,也许,它将内联整个功能。这种情况是否发生无关紧要,因为代码的含义在之前的优化步骤中已被破坏。

与中断(或线程,或DMA,硬件寄存器或回调函数)共享的任何变量必须始终声明为volatile。

答案 2 :(得分:2)

任何对硬件寄存器的访问最好标记为volatile。编译器不知道它将通过硬件中的中断或DMA进行更改,并且编译器可以并且将认为它不会这样,它会缓存某些值。

基本上,如果它是硬件映射的,或者可以通过中断(来自硬件)进行更改,则标记为易失性。

答案 3 :(得分:2)

除了标记变量volatile以强制加载(如@dasblinkenlight建议的那样),您还应该采取措施确保以原子方式读取(和写入)变量。在某些平台上,对于某些大小的对象(如最近的x86处理器上的32位值),这会自动发生。通常,您可能需要在变量周围放置同步锁,例如互斥锁或信号量。当异步代码是一个线程时,这相对容易。当涉及到真正的中断时,我不确定该怎么做,因为某些同步技术可能是不可能的。您的平台文档应该提供一些见解。

答案 4 :(得分:0)

应声明所有共享内存volatile

您的特定编译器在特定示例中不会优化读取可能会或可能不正确,但在这种情况下,volatile关键字会增加开销(即存在指定显式读取在任何情况下都会发生的开销没有开销,那么为什么要冒未定义或不可移植的行为?