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

C 函数调用之间是否刷新了全局变量?,c,gcc,embedded,c99,volatile,C,Gcc,Embedded,C99,Volatile,我正在编写嵌入式固件,有时很难决定何时需要volatile 当我有一个函数等待某个布尔标志被中断更改时,很明显该标志需要是可变的,因为否则该函数将永远等待,因为编译器没有意识到该值可以被中断更改 但是当我有一个短函数,只检查第一行中的标志时,我希望标志不需要是易变的,因为每次我进入该函数时,它的值都会被读取?因此,当一个中断在我第一次调用函数和第二次调用函数之间修改它的值时,我将得到新的值。还是不能保证每次输入函数时所有缓存寄存器都被清除?您仍然需要标记变量volatile:因为优化器可以自由内

我正在编写嵌入式固件,有时很难决定何时需要volatile

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


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

您仍然需要标记变量
volatile
:因为优化器可以自由内联您的函数,尤其是短函数,在循环中调用函数时,如果没有访问硬件修改内存的
volatile
标记,则在初始迭代后,您将面临无法读取内存的危险。

对硬件寄存器的任何访问最好标记为volatile。编译器不知道它将通过硬件中断或DMA进行更改,编译器可以也将假定它不知道,因此它可以也将缓存某些值


基本上,如果它是硬件映射的,或者可以通过中断(从硬件)更改,则将其标记为volatile。

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

…因为每次我输入函数时都会读取它的值

不,这不能保证。“缺少易失性bug”的问题在于编译器的优化器不知道某个变量可以从外部源更改,因此更改了代码的全部含义

所以如果你有这个:

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,我将优化代码以实现这一点:

然后,它可能会内联整个函数。无论这种情况是否发生,都不重要,因为代码的含义在前面的优化步骤中已经被破坏了


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

所有共享内存都应声明为volatile


在特定示例中,您的特定编译器不会优化读取,这可能是正确的,也可能是错误的,但在这种情况下,
volatile
关键字会增加零开销(即,在任何情况下,指定显式读取的位置都不会增加开销),那么,为什么要冒未定义或不可移植行为的风险呢?

但是当我将我的函数标记为“不可内联”时,C是否保证它会得到一个新的副本?@Joshua我怀疑标准中是否有这样的保证,但我想不出一种解决方法:如果函数不可内联,并且有一个外部变量的引用,加载值需要以某种方式进行,因此会有读取。当编译器注意到函数在循环中被调用时,可能会回收一些寄存器/堆栈部分,但我很高兴它们没有那么聪明;)函数是否内联并不重要,因为“缺少volatile”错误更为根本:优化器可能会更改整个代码的含义。所以这并不能回答这个问题。@Lundin为什么,它肯定回答了“我可以跳过volatile”这个问题,给出了一个很好的理由(内联)。它是一个32位ARM处理器,在文档中我读到所有32位访问都是自动原子的。所以我很幸运,因为我不知道如何在这个简单的MCU上实现互斥。听起来你们都准备好了。祝你好运。互斥体和其他可以阻止的操作系统调用不能在中断处理程序中使用。如果正在使用RTOS,从中断处理程序通知等待线程的通常方式是通过信号量信号,而不是一些易失性标志。如果有疑问,请反汇编并查看编译器的操作。这并不意味着如果你改变了任何代码,它总是会或不会改变,每次编译和/或只是使它不稳定的时候必须重新检查。如果<代码>易失性> /代码>效率太低,考虑内存障碍。应该如何处理在代码执行过程中在某些离散点共享但不在其他时间共享的内存?例如,在DMA可以比CPU驱动的循环更快地输出数据的平台上,编写fread/fwrite风格函数的最有效方法可能是直接向调用者的缓冲区执行DMA,但这类函数要求调用者提供
volatile
对象会非常尴尬。虽然在使用DMA之前,代码可以将数据复制到
volatile
缓冲区,但这将首先否定使用DMA的优势。@superat:这是一个非常不同且复杂得多的问题。你应该发布一个自己的问题,而不是在一个对别人的问题几乎不相关的旧答案上添加评论。。
int func (void)
{
  return 1;
}