📜  了解C语言中的“易失性”限定词第2组(示例)

📅  最后修改于: 2021-05-25 22:18:32             🧑  作者: Mango

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

声明为volatile的对象从优化中被省略,因为它们的值可以随时通过当前代码范围之外的代码来更改。系统总是从存储位置读取易失性对象的当前值,而不是在请求时将其值保存在临时寄存器中,即使先前的指令要求从同一对象中获取值。因此,简单的问题是,变量的值如何以编译器无法预测的方式变化。考虑以下情况以回答该问题。

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

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

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

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

/* Compile code without optimization option */
#include 
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
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 731 2016-11-19 16:19 volatile.s
  [narendra@ubuntu]$

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

/* Compile code with optimization option */
#include 
  
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
  Initial value of local : 10
  Modified value of local: 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 
  
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
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 711 2016-11-19 16:22 volatile.s
  [narendra@ubuntu]$

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

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

相关文章:
了解C语言中的“易失性”限定词设置1(简介)

请参阅以下链接以获取关于volatile关键字的更多详细信息:
易失性:程序员最好的朋友
不要使用volatile作为同步原语

想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。