1、volatile简要说明
volatile英文意思为易变的、易挥发的,在声明变量时加入这个关键字,**意思就是告诉编译器这个变量随时能被外部修改,不要对此变量进行优化,代码中引用此变量必须访问内存中实际变量。**有的小伙伴可能看到这里会很懵,CPU访问变量不就是访问内存中的实际变量吗,并不是这样的,CPU内部还有寄存器,CPU读写寄存器的速度是远大于读写内存的速度,为了提高程序的执行速度,编译器会优化代码,把实际的变量存放到寄存器中,通过读写寄存器来访问该变量。
2、引用keil官方的编译器用户指南例程说明
下图有两个函数,左边跟右边函数的区别就是右边函数声明变量buffer_full时加入了volatile关键字。
使用-O2选项来编译上图代码,反汇编后得到下图
分析:左边编译器对buffer_full变量进行了优化,在进入L1.12循环前,先把buffer_full变量的值存放到r1寄存器中,进入while循环后直接使用r1寄存器中的值,并没有访问实际的buffer_full变量,如果buffer_full变量在外部被修改了,程序就会得到意想不到的结果。右边声明变量buffer_full时加入了volatile关键字,每次进入L1.8循环时都会读取内存中buffer_full变量的值并存放到r2寄存器,用r2的值来判断是否继续执行循环,即使某一时刻buffer_full变量在外部被修改了,程序仍然可以达到预期的效果。
3、实验验证
使用GD32单片机,实验方法:执行delay函数(count相同)前后,控制GPIO的电平变化,用示波器抓取波形,测量出函数运行的时间。下面两个函数的区别是下面函数声明变量us 时加入了volatile关键字。
void my_delay(unsigned int count)
{
for(int i = 0; i < count; i++)
{
uchar us = 12;
while (us--) ;
}
}
测试结果如下:
-O0优化下,测得是68us
-O2优化下,测的是47.6us
void my_delay(unsigned int count)
{
for(int i = 0; i < count; i++)
{
volatile uchar us = 12;
while (us--) ;
}
}
测试结果如下:
-O2优化下,测的是86us
实验分析:
同样使用-O2优化等级,加了volatile关键字的delay函数,执行速度变慢了。
加了volatile关键字的delay函数,使用-O2优化等级,执行速度比不加volatile关键字的delay函数(使用-O0优化)还慢,说明了使用-O0优化编译器仍然会作一定优化。
补充实验,声明变量count时也加入了volatile关键字,
void my_delay(volatile unsigned int count);
void my_delay(volatile unsigned int count)
{
for(int i = 0; i < count; i++)
{
volatile uchar us = 12;
while (us--) ;
}
}
测试结果如下:
-O2优化下,测的是107.6us
实验分析:delay函数执行的更慢了
结论:为了避免优化对delay执行速度的影响,需要注意volatile关键字的使用,否则程序可能会在开优化后会产生意想不到的结果。
4、使用volatile的场景
- 访问内存映射的外设
- 多线程共享的全局变量
- 在中断例程中访问的全局变量