volatile关键字

volatile关键字总结

背景

volatile介绍

volatile关键字告诉编译器,不要去寄存器中访问某个变量,而是直接去内存中读取。

volatile关键字的使用场景

volatile常用的场景:

  1. 并行设备的寄存器,如状态寄存器
  2. 多线程程序
  3. 一个中断服务子程序访问到的非自动变量

总结

为什么在中断中修改了i的值,而在main函数中的i没有被编译器认为是1。而如果不是通过中断修改i,而只是在程序中修改i的值,main函数中的i就会被认为是1。

这就类似于:就比如我在盯着一个房间有没有人,我一开始进这个房间看了一眼,没有人,那么这个时候我就只需要在门口盯着,确保没有人从门口进去就行了。这个时候中断来了,就类似时间被暂停了,然后有个人进房间了。然后时间恢复,这个时候我依旧只会盯着门看,不会进门去看一下,确认房间是否有人。因为我认为我一直在盯着门。 

这个就类似一开始编译器从内存中拿出i的值放到寄存器中,然后if(i)反复判断。编译器就会认为我中间没有去做其他事,没有必要再去从内存中拿一次i的值。

而如果再程序中修改i的值,就类似我在看着门,突然有事离开。然后再回来,我要确保我出去的这段时间,有没有人进房间,所以会进房间看一次有没有人。就类似从内存中取一次i的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int i=0;

int main(void)
{
...
while (1)
{
if (i)
{
dosomething();
}
}
}

/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}

上述程序的本意是希望ISR_2中断产生时,在main函数中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。

1
2
3
4
5
6
7
8
9
10
int  *output = (unsigned  int *)0xff800000;//定义一个IO端口;

int init(void)
{
int i;
for(i=0;i< 10;i++){

*output = i;
}
}

没有使用volatile关键字,则该代码会被优化为:

1
2
3
4
int  init(void)
{
*output = 9;
}

所以我们需要使用volatile关键字,来告诉编译器不要优化。

小练习

下面函数有什么问题?

1
2
3
4
void test (int volatile* a)
{
return (*a) * (*a);
}

上面有可能a会被其他的线程所改变。更正:

1
2
3
4
5
void test (int volatile* a)
{
int b = a;
return b * b;
}

总结

通过上面的代码,volatile关键字似乎不仅仅是阻止,编译器优化关于寄存器分配的优化。由第二段代码可知,volatile还可以阻止编译器进行死代码消除的优化。是否可以认为,只要添加了该关键字,就可以认为编译器不会随便对该对象进行优化。

参考资料

https://www.cnblogs.com/zhuangquan/p/15057897.html


volatile关键字
https://ysc2.github.io/ysc2.github.io/2023/11/09/volatile关键字/
作者
Ysc
发布于
2023年11月9日
许可协议