欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

C語言中關(guān)鍵字volatile追根問底

作者:未知   來源:布冬冬 的空間   點(diǎn)擊數(shù):  更新時(shí)間:2014年08月16日   【字體:

volatile 的英文解釋是——“易失的,易改變的。顧名思義,這個(gè)關(guān)鍵字的含義是向編譯器指明變量的內(nèi)容可能會(huì)由于編譯器意想不到的情況的變化而發(fā)生變化這個(gè)解釋仍然比較抽象,感興趣的可以繼續(xù)閱讀下面內(nèi)容。

 
先看一下編譯器對(duì)程序的優(yōu)化過程是怎么進(jìn)行的
如果編譯器在代碼中發(fā)現(xiàn)對(duì)同一地址的兩次訪問之間,沒有對(duì)該地址進(jìn)行寫操作,那么編譯器的優(yōu)化過程認(rèn)為——第一次尋址讀取該地址時(shí)取得的變量的值(編譯器會(huì)盡最大可能把這個(gè)值存放在通用寄存器R中或者cache)作為第二次尋址的值,而并不是再做第二次內(nèi)存的 I/O 尋址操作(當(dāng)CPU把該變量的值放到通用寄存器或者cache中后就不會(huì)再關(guān)心對(duì)應(yīng)內(nèi)存中的值)。
例如:
int i = 10;
int j = i;  // (1)語句
int k = i;  // (2)語句
因?yàn)樵冢?/span>1)、(2)兩條語句中,i沒有被用作左值同一地址的兩次訪問之間,沒有對(duì)該地址進(jìn)行寫操作),這時(shí)候編譯器認(rèn)為i的值沒有發(fā)生變化,所以在(1)語句時(shí),從內(nèi)存中取出i的值賦給j之后,這個(gè)值并沒有丟掉(存放在通用寄存器中),而是在(2)語句時(shí)繼續(xù)用這個(gè)值給k賦值。編譯器不會(huì)生成匯編代碼而重新從內(nèi)存里尋址i的值。
 
Volatile這個(gè)關(guān)鍵字的必要性(真正意義之所在)
但其他程序(例如內(nèi)核程序或一個(gè)中斷)修改了內(nèi)存中該變量的值,此時(shí)寄存器R中的值并不會(huì)隨之改變而更新,由于優(yōu)化器的作用編譯器仍然去利用之前存放在寄存器R中的值,而不去尋址內(nèi)存中的值(但我們必須改變這個(gè)變量的值)。為了解決這種情況C語言就引入了volatile限定詞,讓代碼在引用該變量時(shí)多費(fèi)一點(diǎn)勁兒,再去內(nèi)存中取出該變量的值。
例如:
Volatile int i = 10;
int j = i;  // (3)語句
int k = i; // (4)語句
         這里,volatile關(guān)鍵字告訴編譯器i是隨時(shí)可能發(fā)生變化的,每次使用它的時(shí)候必須從內(nèi)存中取出i的值,因而編譯器生成的匯編代碼會(huì)重新從內(nèi)存中i的地址處讀取i的值存放在k中。
一句話概括就是,當(dāng)用volatile關(guān)鍵字修飾變量時(shí),優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地去內(nèi)存重新讀取(關(guān)鍵之處)這個(gè)變量的值,而不是使用保存在寄存器R里的備份。
 
Volatileregister的對(duì)比
volatile 跟以前的 register 相反。 register 告訴編譯器盡量將變量放到寄存器中使用, volatile 強(qiáng)制將更改后的值寫回內(nèi)存。如果不寫回內(nèi)存,對(duì)于一些全局共享的變量,可能導(dǎo)致不一致問題。
 
volatie變量將和cache不發(fā)生關(guān)系,只和內(nèi)存有關(guān)系
簡單點(diǎn)說就是每次操作前從內(nèi)存取值
volatie修飾的變量,每次操作時(shí)遵循下面動(dòng)作:
從內(nèi)存取值 ---> 放入寄存器 ----> 操作 ----> 寫回內(nèi)存
沒有volatie修飾的變量,操作可能遵循:
從內(nèi)存取值 ---> 放入寄存器 ----> 第一次操作 -----> 第二次操作(此時(shí)仍操作寄存器中的值) …… ----> N次操作 ----> 寫回內(nèi)存
 
舉個(gè)例子論述兩者關(guān)系
int volatie i;  //全局變量,在其它地方會(huì)被修改
 
while (i)
{
do_somethings();
}
如果i沒有被volatie修飾,當(dāng)while循環(huán)執(zhí)行時(shí),另一段程序并發(fā)的執(zhí)行了i = 0 這個(gè)循環(huán)仍不會(huì)退出,因?yàn)槊看窝h(huán)都是檢查寄存器中的值。
如果有volatie修飾,那么循環(huán)結(jié)束,因?yàn)檠h(huán)每次檢查i的時(shí)候,會(huì)先從內(nèi)存把i讀入寄存器,這個(gè)時(shí)候i在其它地方被賦0,則循環(huán)結(jié)束。 
 
Volatile關(guān)鍵字應(yīng)用的三個(gè)地方
1、   修改硬件寄存器,尤其是狀態(tài)寄存器
    大家都知道,在硬件級(jí)別,如果寄存器值自動(dòng)改變了,編譯器是不會(huì)主動(dòng)發(fā)現(xiàn)的。經(jīng)過編譯器的自動(dòng)優(yōu)化,我們讀到的都是寄存器中存儲(chǔ)的舊的狀態(tài)寄存器的值, 而非最新的狀態(tài)寄存器值例如:
l         #define STATUS  (*(volatile unsigned long *)0x56000010)  
2、  多線程中被幾個(gè)線程共享的變量
    線程修改共享變量var是不會(huì)通知編譯器的。所以線程A堅(jiān)持不懈地讀著var在寄存器或者cache中的副本,讀出來的內(nèi)容是0,但很可惜,線程B早就把var變量給修改為1了。鑒于此,我們必須加上volatile這個(gè)關(guān)鍵字來解決這個(gè)問題。
3、  中斷服務(wù)程序ISR當(dāng)中用
    ISR:中斷服務(wù)程序 interrupt service routine
  所謂中斷是指當(dāng)CPU正在處理某件事情的時(shí)候,外部發(fā)生的某一事件(如一個(gè)電平的變化,一個(gè)脈沖沿的發(fā)生或定時(shí)器計(jì)數(shù)溢出等)請(qǐng)求CPU迅速去處理,于是CPU暫時(shí)中止當(dāng)前的工作,轉(zhuǎn)去處理所發(fā)生的事件。中斷服務(wù)處理完該事件以后,再回到原來被中止的地方繼續(xù)原來的工作。
 
volatile引出的三個(gè)問題
1)一個(gè)參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2)一個(gè)指針可以是volatile 嗎?解釋為什么。
3)下面的函數(shù)有什么錯(cuò)誤:
int square(volatile int *ptr)
{return *ptr * *ptr;}
答:
1)是的。一個(gè)典型的個(gè)例子就是只讀的狀態(tài)寄存器
¨          它是volatile因?yàn)樗赡鼙灰庀氩坏降馗淖儭?/span>
¨          它是const因?yàn)槌绦虿粦?yīng)該試圖去修改它。
2)是的。盡管這并不很常見。一個(gè)例子是當(dāng)一個(gè)中服務(wù)子程序修改一個(gè)指向一個(gè)buffer的指針時(shí)。
3)這段代碼的有個(gè)惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個(gè)volatile型參數(shù),

 
編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int *ptr)
{
int a, b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此ab可能是不同的。結(jié)果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
關(guān)閉窗口

相關(guān)文章