C语言volatile关键字

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

                                                           “不妨大胆一些,爱一个人,攀一座山,追一个梦。”----- 大鱼海棠


volatile是一个类型修饰符(type specifier), 防止编译器对代码进行优化。

volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。  volatile应该解释为“直接存取原始内存地址”比较合适。

编译器的优化:

在本次线程内,当读取一个变量时,为提高存取速度,编译器优化时,有时会先把变量读取到一个寄存器中;以后再取变量值时,就直接从寄存器中取值;当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。但是,当变量被别的线程改变时,该寄存器的值不会相应地改变,从而造成应用程序读取的值和实际的变量值不一致。


例子:

XBYTE[2]=0x55;

XBYTE[2]=0x56;

XBYTE[2]=0x57;

XBYTE[2]=0x58;

对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。

精确地说就是,编译器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。


使用volatile变量的几个case

1)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

2)多线程应用中被几个任务共享的变量

这是区分C程序员和嵌入式系统程序员的最基本的问题:嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所有这些都要求使用volatile变量。不懂得volatile内容将会带来灾难。


例子:

volatile int i=10;

int a=i;

//...

//其他代码,并未明确告诉编译器,对i进行过操作

Int b = i;

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

由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。


例子:

static int i = 0;

int main(void){

    //...

    while(1){

        if(i)

            dosomething();

    }

}

/*Interruptserviceroutine.*/

void ISR_2(void){

    i=1;

}

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

 

Ref:  https://baike.baidu.com/item/volatile/10606957?fr=aladdin


Ref:

https://www.cnblogs.com/reality-soul/p/6140192.html

https://www.cnblogs.com/yangguang-it/p/6719261.html?utm_source=itdadao&utm_medium=referral

https://www.cnblogs.com/god-of-death/p/7852394.html

猜你喜欢

转载自blog.csdn.net/qq_35865125/article/details/88937682