volatile关键字旨在防止编译器对可能以编译器无法确定的方式更改的对象应用任何优化。

声明为volatile的对象在优化中被省略,因为它们的值可以随时由当前代码范围之外的代码更改。系统始终从内存位置读取易失性对象的当前值,而不是将其值保存在请求点的临时寄存器中,即使前一条指令要求来自同一对象的值也是如此。所以简单的问题是,变量的值如何以编译器无法预测的方式改变。考虑以下案例来回答这个问题。

1)由范围之外的中断服务例程修改的全局变量:
例如,全局变量可以表示将动态更新的数据端口(通常称为内存映射IO的全局指针)。必须将代码读取数据端口声明为volatile,以便获取端口上可用的最新数据。如果没有将变量声明为volatile,编译器将优化代码,使其只读取一次端口并在临时寄存器中保持使用相同的值来加速程序(速度优化)。通常,由于新数据的可用性,当存在中断时,ISR用于更新这些数据端口

2)多线程应用程序中的全局变量:线程通信有多种方式,即消息传递,共享内存,邮箱等。全局变量是共享内存的弱形式。当两个线程通过全局变量共享信息时,它们需要使用volatile进行限定。由于线程是异步运行的,因此应该由另一个消费者线程新获取由于一个线程引起的全局变量的任何更新。编译器可以读取全局变量,并可以将它们放在当前线程上下文的临时变量中。为了抵消编译器优化的影响,这些全局变量被限定为volatile

如果我们不使用volatile限定符,可能会出现以下问题:
1)打开优化时,代码可能无法按预期工作。
2)启用和使用中断时,代码可能无法正常工作。

让我们看一个例子来理解编译器如何解释volatile关键字。考虑下面的代码,我们使用指针更改const对象的值,我们正在编译没有优化选项的代码。因此编译器不会进行任何优化,并会更改const对象的值。

/* Compile code without optimization option */
#include <stdio.h>
int main(void)
{
    const int local = 10;
    int *ptr = (int*) &local;
    printf("Initial value of local : %d \n", local);
    *ptr = 100;
    printf("Modified value of local: %d \n", local);
    return 0;
}

当我们使用gcc的“-save-temps”选项编译代码时,它会生成3个输出文件

1)预处理代码(具有.i扩展名)
2)汇编代码(具有.s扩展名)和
3)目标代码(具有.o选项)。

我们在没有优化的情况下编译代码,这就是为什么汇编代码的大小会更大(下面用红色突出显示)。

输出:

  [narendra @ ubuntu] $ gcc volatile.c -o volatile -save-temps
  [narendra @ ubuntu] $ ./volatile
  本地的初始值:10
  修改后的本地值:100
  [narendra @ ubuntu] $ ls -l volatile.s
  -rw-r-r- 1 narendra narendra 731 2016-11-19 16:19 volatile.s
  [纳伦德拉@求助] $

让我们使用优化选项(即-O选项)编译相同的代码。在下面的代码中,“local”被声明为const(和非易失性),GCC编译器进行优化并忽略试图改变const对象值的指令。因此const对象的值保持不变。

/* Compile code with optimization option */
#include <stdio.h>
int main(void)
{
    const int local = 10;
    int *ptr = (int*) &local;
    printf("Initial value of local : %d \n", local);
    *ptr = 100;
    printf("Modified value of local: %d \n", local);
    return 0;
}

对于上面的代码,编译器会进行优化,这就是汇编代码大小减小的原因。

输出:

  [narendra @ ubuntu] $ gcc -O3 volatile.c -o volatile -save-temps
  [narendra @ ubuntu] $ ./volatile
  本地的初始值:10
  修改后的本地值:10
  [narendra @ ubuntu] $ ls -l volatile.s
  -rw-r-r- 1 narendra narendra 626 2016-11-19 16:21 volatile.s

让我们将const对象声明为volatile并使用优化选项编译代码。虽然我们使用优化选项编译代码,但const对象的值将会改变,因为变量被声明为volatile,这意味着不进行任何优化。

/* Compile code with optimization option */
#include <stdio.h>
int main(void)
{
    const volatile int local = 10;
    int *ptr = (int*) &local;
    printf("Initial value of local : %d \n", local);
    *ptr = 100;
    printf("Modified value of local: %d \n", local);
    return 0;
}

输出:

  [narendra @ ubuntu] $ gcc -O3 volatile.c -o volatile -save-temp
  [narendra @ ubuntu] $ ./volatile
  本地的初始值:10
  修改后的本地值:100
  [narendra @ ubuntu] $ ls -l volatile.s
  -rw-r-r- 1 narendra narendra 711 2016-11-19 16:22 volatile.s
  [纳伦德拉@求助] $

上面的例子可能不是一个很好的实际例子,目的是解释编译器如何解释volatile关键字。作为一个实际的例子,想想手机上的触摸传感器。抽象触摸传感器的驱动程序将读取触摸位置并将其发送到更高级别的应用程序。驱动程序本身不应修改(const-ness)读取位置,并确保每次刷新时读取触摸输入(volatile-ness)。这种驱动器必须以const volatile方式读取触摸传感器输入。

注意:以上代码是特定于编译器的,可能不适用于所有编译器。这些例子的目的是让读者理解这个概念。

 

perm_identity 理解C中的“volatile”限定符 第2组(示例)-IDC帮帮忙