C++ volatile 關(guān)鍵字

注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境
volatile關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改创夜。

用volatile關(guān)鍵字聲明的變量i每一次被訪問時(shí),執(zhí)行部件都會(huì)從i相應(yīng)的內(nèi)存單元中取出i的值熬甚。

沒有用volatile關(guān)鍵字聲明的變量i在被訪問的時(shí)候可能直接從cpu的寄存器中取值(因?yàn)橹癷被訪問過冤狡,也就是說之前就從內(nèi)存中取出i的值保存到某個(gè)寄存器中),之所以直接從寄存器中取值递雀,而不去內(nèi)存中取值柄延,是因?yàn)榫幾g器優(yōu)化代碼的結(jié)果(訪問cpu寄存器比訪問ram快的多)。

以上兩種情況的區(qū)別在于被編譯成匯編代碼之后缀程,兩者是不一樣的搜吧。之所以這樣做是因?yàn)樽兞縤可能會(huì)經(jīng)常變化,保證對(duì)特殊地址的穩(wěn)定訪問杨凑。

volatile關(guān)鍵字是一種類型修飾符滤奈,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統(tǒng)撩满、硬件或者其它線程等蜒程。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問該變量的代碼就不再進(jìn)行優(yōu)化鹦牛,從而可以提供對(duì)特殊地址的穩(wěn)定訪問搞糕。

使用該關(guān)鍵字的例子如下:

volatile int nVint; 

當(dāng)要求使用volatile 聲明的變量的值的時(shí)候,系統(tǒng)總是重新從它所在的內(nèi)存讀取數(shù)據(jù)曼追,即使它前面的指令剛剛從該處讀取過數(shù)據(jù)窍仰。而且讀取的數(shù)據(jù)立刻被保存。

例如:

volatile int i=10; 
int a = i; 
... 
//其他代碼礼殊,并未明確告訴編譯器驹吮,對(duì)i進(jìn)行過操作 
int b = i; 

volatile 指出 i是隨時(shí)可能發(fā)生變化的针史,每次使用它的時(shí)候必須從i的地址中讀取,因而編譯器生成的匯編代碼會(huì)重新從i的地址讀取數(shù)據(jù)放在b中碟狞。而優(yōu)化做法是啄枕,由于編譯器發(fā)現(xiàn)兩次從i讀數(shù)據(jù)的代碼之間的代碼沒有對(duì)i進(jìn)行過操作,它會(huì)自動(dòng)把上次讀的數(shù)據(jù)放在b中族沃。而不是重新從i里面讀频祝。這樣以來,如果i是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)就容易出錯(cuò)脆淹,所以說volatile可以保證對(duì)特殊地址的穩(wěn)定訪問常空。
  
注意,在vc6中盖溺,一般調(diào)試模式?jīng)]有進(jìn)行代碼優(yōu)化漓糙,所以這個(gè)關(guān)鍵字的作用看不出來。下面通過插入?yún)R編代碼烘嘱,測(cè)試有無volatile關(guān)鍵字昆禽,對(duì)程序最終代碼的影響:
  
首先,用classwizard建一個(gè)win32 console工程蝇庭,插入一個(gè)voltest.cpp文件醉鳖,輸入下面的代碼:

  #include <stdio.h> 
  void main() 
  { 
    int i=10; 
    int a = i; 
    printf("i= %d\n",a); 
    //下面匯編語句的作用就是改變內(nèi)存中i的值,但是又不讓編譯器知道 
    __asm { 
        mov dword ptr [ebp-4], 20h 
    } 
    int b = i; 
    printf("i= %d\n",b); 
  }

然后遗契,在調(diào)試版本模式運(yùn)行程序辐棒,輸出結(jié)果如下:
  i = 10
  i = 32

然后病曾,在release版本模式運(yùn)行程序牍蜂,輸出結(jié)果如下:
  i = 10
  i = 10

輸出的結(jié)果明顯表明,release模式下泰涂,編譯器對(duì)代碼進(jìn)行了優(yōu)化鲫竞,第二次沒有輸出正確的i值。

下面逼蒙,我們把 i的聲明加上volatile關(guān)鍵字从绘,看看有什么變化:

  #include <stdio.h> 
  void main()
  { 
    volatile int i=10; 
    int a = i; 
    printf("i= %d\n",a); 
    __asm { 
        mov dword ptr [ebp-4], 20h 
    } 
    int b = i; 
    printf("i= %d\n",b); 
  }

分別在調(diào)試版本和release版本運(yùn)行程序,輸出都是:
  i = 10
  i = 32

這說明這個(gè)關(guān)鍵字發(fā)揮了它的作用是牢!

關(guān)鍵字volatile有什么含意?并給出三個(gè)不同的例子僵井。
一個(gè)定義為volatile的變量是說這變量可能會(huì)被意想不到地改變,這樣驳棱,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了批什。精確地說就是,優(yōu)化器在用到這個(gè)變量時(shí)必須每次都小心地重新讀取這個(gè)變量的值社搅,而不是使用保存在寄存器里的備份驻债。 下面是volatile變量的幾個(gè)例子:

  1. 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
  2. 一個(gè)中斷服務(wù)子程序中會(huì)訪問到的非自動(dòng)變量(Non-automatic variables)
  3. 多線程應(yīng)用中被幾個(gè)任務(wù)共享的變量

搞嵌入式的家伙們經(jīng)常同硬件乳规、中斷、RTOS等等 打交道合呐,所有這些都要求用到volatile變量暮的。不懂得volatile的內(nèi)容將會(huì)帶來災(zāi)難。

1)一個(gè)參數(shù)既可以是const還可以是 volatile嗎淌实?解釋為什么冻辩。
2); 一個(gè)指針可以是volatile 嗎?解釋為什么拆祈。
3); 下面的函數(shù)有什么錯(cuò)誤:

int square(volatile int *ptr)
{
        return *ptr * *ptr;
}

下面是答案:
1)是的微猖。例如只讀的狀態(tài)寄存器。

它是volatile因?yàn)樗赡鼙灰庀氩坏降馗淖儭?/p>

它是const因?yàn)槌绦虿粦?yīng)該試圖 去修改它缘屹。
2)是的凛剥。

盡管這并不很常見。一個(gè)例子是當(dāng)一個(gè)中服務(wù)子程序修該一個(gè)指向一個(gè)buffer的指針時(shí)轻姿。

這段代碼有點(diǎn)變態(tài)犁珠。

這段代碼的目的是用來返指針ptr指向值的平方,但是互亮,由于ptr指向一個(gè)volatile型參數(shù)犁享,編譯器將產(chǎn)生類似下面的代碼:

int square(volatile int *ptr) 
{
    int a,b;
    a = *ptr;
    b = *ptr;
    return a * b;
}

由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的豹休。結(jié)果炊昆,這段代碼可能返不是你所期望的平方值!正確的代碼如下:

long square(volatile int *ptr) 
{
    int a;
    a = *ptr;
    return a * a;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末威根,一起剝皮案震驚了整個(gè)濱河市凤巨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洛搀,老刑警劉巖敢茁,帶你破解...
    沈念sama閱讀 222,946評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異留美,居然都是意外死亡彰檬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門谎砾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逢倍,“玉大人,你說我怎么就攤上這事景图〗系瘢” “怎么了?”我有些...
    開封第一講書人閱讀 169,716評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵症歇,是天一觀的道長郎笆。 經(jīng)常有香客問我谭梗,道長,這世上最難降的妖魔是什么宛蚓? 我笑而不...
    開封第一講書人閱讀 60,222評(píng)論 1 300
  • 正文 為了忘掉前任激捏,我火速辦了婚禮,結(jié)果婚禮上凄吏,老公的妹妹穿的比我還像新娘远舅。我一直安慰自己,他們只是感情好痕钢,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,223評(píng)論 6 398
  • 文/花漫 我一把揭開白布图柏。 她就那樣靜靜地躺著,像睡著了一般任连。 火紅的嫁衣襯著肌膚如雪蚤吹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,807評(píng)論 1 314
  • 那天随抠,我揣著相機(jī)與錄音裁着,去河邊找鬼。 笑死拱她,一個(gè)胖子當(dāng)著我的面吹牛二驰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秉沼,決...
    沈念sama閱讀 41,235評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼桶雀,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了唬复?” 一聲冷哼從身側(cè)響起矗积,我...
    開封第一講書人閱讀 40,189評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盅抚,沒想到半個(gè)月后漠魏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倔矾,經(jīng)...
    沈念sama閱讀 46,712評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妄均,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,775評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哪自。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丰包。...
    茶點(diǎn)故事閱讀 40,926評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖壤巷,靈堂內(nèi)的尸體忽然破棺而出邑彪,到底是詐尸還是另有隱情,我是刑警寧澤胧华,帶...
    沈念sama閱讀 36,580評(píng)論 5 351
  • 正文 年R本政府宣布寄症,位于F島的核電站宙彪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏有巧。R本人自食惡果不足惜释漆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,259評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篮迎。 院中可真熱鬧男图,春花似錦、人聲如沸甜橱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岂傲。三九已至难裆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镊掖,已是汗流浹背差牛。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留堰乔,地道東北人偏化。 一個(gè)月前我還...
    沈念sama閱讀 49,368評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像镐侯,于是被迫代替她去往敵國和親侦讨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,930評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容