C语言中volatile关键字的深入讲解
1. 什么是volatile关键字?
volatile用于声明一个变量,告诉编译器该变量值容易发生改变,在编译、读取、存储该变量的时候都不要做任何优化,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取存储数据,不做优化,在我们做嵌入式开发的时候,该关键字作用很大,因为有时变量地址有可能是系统的一个外设地址,他的值的变化并不在程序控制范围内,随时有可能变化,因此需要对他进行声明,每次读取或者存储直接对地址进行操作,而不经过其他中间商,下面我以一个例子来分析vilatile的作用:
2. GCC优化等级
在看例子前先了解一下GCC的优化概念,通常C语言的编译器使用的是GCC编译器,他有个以下几个等级的优化
- -O0 :(默认):没有优化。
- -O或-O1 :优化,但不要花费太多的时间。
- -O2 :更积极地优化
- -O3 :最积极地优化
- -Ofast :最高级的优化
- -Os : 优化代码大小
- -Og :在尽量不干扰调试的情况下优化
开启不同的优化等级,程序编译后的结果就会发生一定的变化,而volatile就是在开启优化的情况下使用,保护变量不被优化
3.volatile声明变量测试
我们先写一段c程序
#include <stdio.h> int main(void) { volatile int i=0; i=1; i=2; i=3; printf("%d",i); return 0; }
程序创建一个变量i,对i进行三次赋值,使用-O默认优化等级对代码进行编译,查看汇编结果,我们可以看到优化后,i=1和i=2被优化,没有生成之间汇编代码,直接取最后的值寻址赋值给i
当我们使用Volatile声明变量后
#include <stdio.h> int main(void) { volatile int i=0; i=1; i=2; i=3; printf("%d",i); return 0; }
编译如下,可以看到变量i的每一个赋值都生成了汇编代码,没有被优化,每次赋值都重新寻址赋值
当我们使用volatile对一个映射到外部寄存器的地址进行声明后,如果像上面连续赋值来传数据给外部寄存器,使用volatile声明就可以保护指令不会被优化,程序实现正常功能,当然除了用于对寄存器的保护外,volatile有时也使用在多线程间共享变量的保护,如果不注意,编译器会认为变量只在当前函数使用而进行优化,在其他任务中进行的改变就变的无效;如果中断中改变一个状态变量给其他函数进行检测,如果没有加volatile进行声明,也可以会因为编译器的优化而无效,所以大家在以上三种情况下使用变量时可以结合编译器的优化等级考虑一下,是不是需要将变量声明一下volatile关键字!!!