了解C语言中的volatile限定符第2组(示例)

2021年3月10日16:17:55 发表评论 643 次浏览

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

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

1)范围外的中断服务程序修改的全局变量:

例如, 全局变量可以表示将动态更新的数据端口(通常是称为内存映射IO的全局指针)。必须将代码读取数据端口声明为易失性, 以便获取该端口上可用的最新数据。未能将变量声明为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
  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 <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
  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 <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
  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作为同步原语

本文由" Narendra Kangralkar"编辑, 并由lsbin团队审阅。如果发现任何不正确的地方, 或者想分享有关上述主题的更多信息, 请写评论。

木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: