所有在C中都使用volatile关键字

时间:2017-05-29 13:04:20

标签: c embedded driver volatile

我知道volatile关键字会阻止编译器优化变量,并在读取时从内存中读取它。除了内存映射寄存器之外,我们需要使用volatile的所有情况是什么?给定一个符合标准的编译器,我是否必须在两种情况下都将test_var声明为volatile?

1

在file1.c

int test_var=100;


void func1()
{
    test_var++;
}

在file2.c

extern int test_var;

void func2()
{
    if(test_var==100)
    {
      ....
    }
}

2

在file1.c

int test_var=100;

void func1()
{

}

在file2.c

extern int test_var;

void func2()
{
    if(test_var==100)
    {
      ....
    }
}

5 个答案:

答案 0 :(得分:6)

内存映射I / O是C中volatile唯一泛型用法。*)

使用POSIX信号,volatile也可以与类型sig_atomic_t一起使用,如下所示:

volatile sig_atomic_t signal_occured = 0;

您的任何一种方案都不应该要求volatile。如果你感兴趣的是保证在不同的编译单元之间更新值,请参阅tofro的评论,extern已经保证了这一点。特别是,volatile 不是正确的工具用于C中的线程同步。它只会引入错误,因为,正如您所说,确实需要实际读取并且写入对变量的访问,但它确实强制执行关于线程的正确排序(它缺少内存障碍,google了解详细信息)。

请注意,这与其他语言不同,volatile旨在在线程之间工作。

在嵌入式系统中,volatile可能足以在ISR(中断服务例程)和主程序之间进行通信,当与原子读取/写入的数据类型结合时,就像{{1}一样对于POSIX信号。请参阅编译器的文档。

*)C标准提到了这一点,以及“异步中断函数”的用例,仅在脚注中,因为内存映射的I / O超出了语言的范围。该语言只是以一种适合内存映射I / O的方式定义sig_atomic_t的语义。

答案 1 :(得分:4)

在您的两个示例中都不需要volatile

volatile是必要的:

  1. 可以在单个执行线程的控制之外更改变量的任何地方,
  2. 即使在语义上没有效果,也需要进行变量访问。
  3. 案例1包括:

    • 内存映射I / O寄存器,
    • 用于DMA传输的内存,
    • 中断和/或线程上下文之间共享的内存,
    • 独立处理器(如双端口RAM)之间共享的内存

    案例2包括:

    • 用于空延迟循环的循环计数器,其中整个循环可以完全优化掉并且不花时间,
    • 在调试器中写入但从未读过的变量。

    以上示例可能并非详尽无遗,但volatile的语义是关键;该语言只需执行显式访问,如源代码所示。

答案 2 :(得分:1)

除了内存映射设备之类的扩展外,标准C volatile还有两个用例:与信号处理程序的交互以及setjmp/longjmp使用对象的修改。两种情况都是因为优化器可能不知道有异常的控制流。

答案 3 :(得分:1)

在使用中断的C单片机应用程序中,volatile关键字必需,以确保中断中设置的值在中断中正确保存,之后在中断中具有正确的值主要处理循环。未能使用volatile可能是基于定时器的中断或基于ADC(模数转换)的中断例如在中断后返回处理器状态后恢复控制流时会出现损坏值的最大原因。来自Atmel和GCC的规范模板:

volatile uint8_t flag = 0;

ISR(TIMER_whatever_interrupt)
{
    flag = 1;
}

while(1) // main loop
{
    if (flag == 1)
    {
        <do something>
        flag = 0;
    }
}

如果没有volatile,则保证按预期工作。

答案 4 :(得分:0)

  

除内存映射寄存器外,在什么情况下   我们需要使用挥发物吗?

如果

  1. 执行是完全顺序的(没有线程,也没有异步传递的信号);
  2. 您不使用longjmp;
  3. 您不需要调试通过优化编译的程序;
  4. 您不会使用具有模糊指定语义的构造,例如浮点运算;
  5. 您不会像基准循环那样进行无用的计算(忽略结果的计算);
  6. 您不执行任何纯计算的计时,即不基于I / O的任何事情(基于I / O,例如网络请求访问,外部数据库访问的计时)

那么您可能就不需要挥发了。