您的位置 首页 知识

volatile的变量运用

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变

一个界说为volatile的变量是说这变量或许会被意想不到地改动,这样,编译器就不会去假定这个变量的值了。精确地说便是,优化器在用到这个变量时有必要每次都小心肠从头读取这个变量的值,而不是运用保存在寄存器里的备份。下面是volatile变量的几个比方:

1).并行设备的硬件寄存器(如:状况寄存器)
2).一个中止服务子程序中会拜访到的非主动变量(Non-automaticvariables)
3).多线程运用中被几个使命同享的变量

答复不出这个问题的人是不会被雇佣的。我以为这是区别C程序员和嵌入式体系程序员的最基本的问题。嵌入式体系程序员常常同硬件、中止、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾祸。
假定被面试者正确地答复了这是问题(嗯,置疑这否会是这样),我将略微深究一下,看一下这家伙是不是直正懂得volatile彻底的重要性。

1).一个参数既可所以const还可所以volatile吗?解说为什么。
2).一个指针可所以volatile吗?解说为什么。
3).下面的函数有什么过错:
intsquare(volatileint*ptr)
{
return*ptr**ptr;
}

下面是答案:
1).是的。一个比方是只读的状况寄存器。它是volatile由于它或许被意想不到地改动。它是const由于程序不应该企图去修正它。
2).是的。虽然这并不很常见。一个比方是当一个中服务子程序修该一个指向一个buffer的指针时。
3).这段代码的有个恶作剧。这段代码的意图是用来返指针*ptr指向值的平方,可是,由于*ptr指向一个volatile型参数,编译器将发生相似下面的代码:
intsquare(volatileint*ptr)
{
inta,b;
a=*ptr;
b=*ptr;
returna*b;
}
由于*ptr的值或许被意想不到地该变,因而a和b或许是不同的。成果,这段代码或许返不是你所期望的平方值!正确的代码如下:
longsquare(volatileint*ptr)
{
inta;
a=*ptr;
returna*a;
}

关键在于两个当地:

1.编译器的优化(请高手帮我看看下面的了解)

在本次线程内,当读取一个变量时,为进步存取速度,编译器优化时有时会先把变量读取到一个寄存器中;今后,再取变量值时,就直接从寄存器中取值;

当变量值在本线程里改动时,会一起把变量的新值copy到该寄存器中,以便保持一致

当变量在因其他线程等而改动了值,该寄存器的值不会相应改动,然后形成运用程序读取的值和实践的变量值不一致

当该寄存器在因其他线程等而改动了值,原变量的值不会改动,然后形成运用程序读取的值和实践的变量值不一致

举一个不太精确的比方:

发薪资时,管帐每次都把职工叫来挂号他们的银行卡号;一次管帐为了省劲,没有即时挂号,用了曾经挂号的银行卡号;刚好一个职工的银行卡丢了,已挂失该银行卡号;然后形成该职工领不到薪酬

职工--原始变量地址
银行卡号--原始变量在寄存器的备份

2.在什么状况下会呈现(如1楼所说)

1).并行设备的硬件寄存器(如:状况寄存器)
2).一个中止服务子程序中会拜访到的非主动变量(Non-automaticvariables)
3).多线程运用中被几个使命同享的变量

弥补:volatile应该解说为“直接存取原始内存地址”比较适宜,“易变的”这种解说几乎有点误导人;

“易变”是由于外在要素引起的,象多线程,中止等,并不是由于用volatile润饰了的变量便是“易变”了,假设没有外因,即运用volatile界说,它也不会改动;

而用volatile界说之后,其实这个变量就不会因外因而改动了,能够放心运用了;咱们看看前面那种解说(易变的)是不是在误导人

------------简明示例如下:------------------

volatile关键字是一种类型润饰符,用它声明的类型变量标明能够被某些编译器不知道的要素更改,比方:操作体系、硬件或许其它线程等。遇到这个关键字声明的变量,编译器对拜访该变量的代码就不再进行优化,然后能够供给对特别地址的安稳拜访。
运用该关键字的比方如下:
intvolatilenVint;
>>>>当要求运用volatile声明的变量的值的时分,体系总是从头从它地点的内存读取数据,即便它前面的指令刚刚从该处读取过数据。并且读取的数据马上被保存。
例如:
volatileinti=10;
inta=i;

//其他代码,并未清晰告知编译器,对i进行过操作
intb=i;
>>>>volatile指出i是随时或许发生改动的,每次运用它的时分有必要从i的地址中读取,因而编译器生成的汇编代码会从头从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会主动把前次读的数据放在b中。而不是从头从i里边读。这样以来,假如i是一个寄存器变量或许标明一个端口数据就简单犯错,所以说volatile能够确保对特别地址的安稳拜访。
>>>>留意,在vc6中,一般调试形式没有进行代码优化,所以这个关键字的效果看不出来。下面经过刺进汇编代码,测验有无volatile关键字,对程序终究代码的影响:
>>>>首要,用classwizard建一个win32console工程,刺进一个voltest.cpp文件,输入下面的代码:
>>
#i nclude
voidmain()
{
inti=10;
inta=i;
printf(“i=%d”,a);
//下面汇编句子的效果便是改动内存中i的值,可是又不让编译器知道
__asm{
movdwordptr[ebp-4],20h
}
intb=i;
printf(“i=%d”,b);
}
然后,在调试版别形式运转程序,输出成果如下:
i=10
i=32
然后,在release版别形式运转程序,输出成果如下:
i=10
i=10
输出的成果显着标明,release形式下,编译器对代码进行了优化,第2次没有输出正确的i值。下面,咱们把i的声明加上volatile关键字,看看有什么改动:
#i nclude
voidmain()
{
volatileinti=10;
inta=i;
printf(“i=%d”,a);
__asm{
movdwordptr[ebp-4],20h
}
intb=i;
printf(“i=%d”,b);
}
分别在调试版别和release版别运转程序,输出都是:
i=10
i=32
这阐明这个关键字发挥了它的效果!

------------------------------------

volatile对应的变量或许在你的程序自身不知道的状况下发生改动
比方多线程的程序,一起拜访的内存傍边,多个程序都能够操作这个变量
你自己的程序,是无法断定适宜这个变量会发生改动
还比方,他和一个外部设备的某个状况对应,当外部设备发生操作的时分,经过驱动程序和中止事情,体系改动了这个变量的数值,而你的程序并不知道。
关于volatile类型的变量,体系每次用到他的时分都是直接从对应的内存傍边提取,而不会运用cache傍边的原有数值,以习惯它的不知道何时会发生的改动,体系对这种变量的处理不会做优化——明显也是由于它的数值随时都或许改动的状况。

——————————————————————————–

典型的比方
for(inti=0;i<100000;i++);
这个句子用来测验空循环的速度的
可是编译器必定要把它优化掉,底子就不履行
假如你写成
for(volatileinti=0;i<100000;i++);
它就会履行了

volatile的原意是“易变的”
由于拜访寄存器的速度要快过RAM,所以编译器一般都会作削减存取外部RAM的优化。比方:

staticinti=0;

intmain(void)
{

while(1)
{
if(i)dosomething();
}
}


voidISR_2(void)
{
i=1;
}

程序的原意是期望ISR_2中止发生时,在main傍边调用dosomething函数,可是,由于编译器判别在main函数里边没有修正过i,因而
或许只履行一次对从i到某寄存器的读操作,然后每次if判别都只运用这个寄存器里边的“i副本”,导致dosomething永久也不会被
调用。假如将将变量加上volatile润饰,则编译器确保对此变量的读写操作都不会被优化(必定履行)。此例中i也应该如此阐明。

一般说来,volatile用在如下的几个当地:

1、中止服务程序中修正的供其它程序检测的变量需求加volatile;

2、多使命环境下各使命间同享的标志应该加volatile;

3、存储器映射的硬件寄存器一般也要加volatile阐明,由于每次对它的读写都或许由不同含义;

别的,以上这几种状况常常还要一起考虑数据的完整性(彼此相关的几个标志读了一半被打断了重写),在1中能够经过关中止来实
现,2中能够制止使命调度,3中则只能依托硬件的杰出规划了。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/257563.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部