关于volatile

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gin_love/article/details/51162459

今天课上遇到volatile unsigned char 不怎么懂volatile的用法/(ㄒoㄒ)/~~,所以特整理此文章方便以后翻看。
一、volatile的含义
volatile:adj 易变的;不稳定的。
volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行编译优化,从而可以提供对特殊地址的稳定访问。

二、编译优化?
之前不理解编译优化,举个例子便清楚多了。编译在不同的模式下出来的结果会有所不同。因此先说下Debug和Release 模式下编译方式的区别,Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
例:
int *a; int b;
b = (a) (*a);这种情况。
通常编译器为了减少存储器的读写时间,会把代码优化为:
int *a; int b; int c;
c = *a;
b = c * c;
即:编译器将(*a)的值给变量c,而变量c的值直接保存在某个寄存器中。由于访问寄存器的速度要快过直接访问内存的速度(外部存储器的读写速度赶不上内存的读写速度),所以编译器通常都会选择减少对于内存的访问,这样优化之后就可以省一次外部存储器的读取时间,从而提高速度。

三、volatile的作用
volatile修饰符的作用就是告诉编译器不要对它所修饰的变量进行任何的优化,每次取值都要直接从内存地址得到。
即如果将某个变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化。
通常在代码中我们为了防止一个变量在意想不到的情况下被改变,我们会将变量定义为volatile,这从而就使得编译器不会自作主张去“动”这个变量的值了。准确点说就是每次在用到这个变量时必须每次都重新从内存中直接读取这个变量的值,而不是使用保存在寄存器里的备份。

例如:
volatile int i=10;
int a = i;
...
//其他代码,并未明确告诉编译器,对i进行过操作
int b = i;

  volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从保存 i 的地址中读取,因而编译器生成的汇编代码会重新从保存 i 的地址读取数据放在b中。
  而优化做法是,由于编译器发现两次读取数据 i 的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在b中。而不是重新从保存 i 的地址里重新读取 i 的值。这样的话,如果 i 是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。
  当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。

再看看下面的代码,在此就简要的写出几步了。
main()
{
int i=o;
while(i==0)
{
……
}
}
分析以上代码,如果我们没有在while循环体结构里面改变i的值,编译器在编译的过程中就会将i的值备份到一个寄存器中,每次执行判断语句时就从该寄存器取值,那么这将是一个死循环,但是如果我们做如下的修改:
main()
{
int volatile i=o;
while(i==0)
{
……
}
}
我们在i的前面加上了一个volatile,假设while()循环体里面执行的是跟上一个完全一样的操作,但是这个时候就不能说是一个死循环了,因为编译器不会再对我们的 i 的值进行”备份”操作了,每次执行判断的时候都会直接从保存 i 的内存地址中读取,一旦其值发生变化就退出循环体。

四、实际使用中volatile的场合
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义。

猜你喜欢

转载自blog.csdn.net/gin_love/article/details/51162459