volatile关键字用于防止编译器对可能以编译器无法确定的方式更改的对象应用任何优化。
声明为volatile的对象在优化中被省略,因为它们的值可以随时由当前代码范围之外的代码更改。系统总是从内存位置读取易失性对象的当前值,而不是在请求时将其值保存在临时寄存器中,即使之前的指令要求从同一对象获取值。所以一个简单的问题是,一个变量的值如何以编译器无法预测的方式变化。考虑下面的案例来回答这个问题。
1) 由范围外的中断服务例程修改的全局变量: 例如,全局变量可以表示将动态更新的数据端口(通常称为内存映射IO的全局指针)。为了获取端口上可用的最新数据,必须将代码读取数据端口声明为volatile。如果没有将变量声明为volatile,编译器将优化代码,使其只读取端口一次,并在临时寄存器中继续使用相同的值来加速程序(速度优化)。通常,当由于新数据的可用性而出现中断时,ISR用于更新这些数据端口
2) 多线程应用程序中的全局变量: 线程通信有多种方式,即消息传递、共享内存、邮箱等。全局变量是共享内存的弱形式。当两个线程通过全局变量共享信息时,它们需要使用volatile限定。由于线程是异步运行的,任何由于一个线程而导致的全局变量更新都应该由另一个使用者线程重新获取。编译器可以读取全局变量,并将其放入当前线程上下文的临时变量中。为了消除编译器优化的影响,此类全局变量需要限定为volatile
如果不使用volatile限定符,可能会出现以下问题 1) 打开优化时,代码可能无法按预期工作。 2) 启用和使用中断时,代码可能无法按预期工作。
让我们看一个例子来了解编译器如何解释volatile关键字。考虑下面的代码,我们正在使用指针改变const对象的值,并且我们编译代码没有优化选项。因此,编译器不会进行任何优化,而是会更改const对象的值。
C
/* 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 " , local); *ptr = 100; printf ( "Modified value of local: %d " , 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选项)编译相同的代码。在下面的代码中,“local”被声明为const(非易失性),GCC编译器进行优化,并忽略试图更改const对象值的指令。所以const对象的值保持不变。
C
/* 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 " , local); *ptr = 100; printf ( "Modified value of local: %d " , 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,这意味着不进行任何优化。
C
/* 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 " , local); *ptr = 100; printf ( "Modified value of local: %d " , 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关键字的更多详细信息,请参阅以下链接: Volatile:程序员最好的朋友 不要将volatile用作同步原语 本文由“Narendra Kangralkar”编辑,Geeksforgeks团队审阅。如果您发现任何不正确的地方,请发表评论,或者您想分享有关上述主题的更多信息。