站長(zhǎng)資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        C/C++ 中 volatile 關(guān)鍵字詳解


        1、為什么用volatile?

        C/C++ 中的 volatile 關(guān)鍵字和 const 對(duì)應(yīng),用來(lái)修飾變量,通常用于建立語(yǔ)言級(jí)別的 memory barrier。這是 BS 在 “The C++ Programming Language” 對(duì) volatile 修飾詞的說(shuō)明:

        A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.

        volatile 關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統(tǒng)、硬件或者其它線程等。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問(wèn)該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的穩(wěn)定訪問(wèn)。聲明時(shí)語(yǔ)法:int volatile vInt; 當(dāng)要求使用 volatile 聲明的變量的值的時(shí)候,系統(tǒng)總是重新從它所在的內(nèi)存讀取數(shù)據(jù),即使它前面的指令剛剛從該處讀取過(guò)數(shù)據(jù)。而且讀取的數(shù)據(jù)立刻被保存。例如:

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

        volatile 指出 i 是隨時(shí)可能發(fā)生變化的,每次使用它的時(shí)候必須從 i的地址中讀取,因而編譯器生成的匯編代碼會(huì)重新從i的地址讀取數(shù)據(jù)放在 b 中。而優(yōu)化做法是,由于編譯器發(fā)現(xiàn)兩次從 i讀數(shù)據(jù)的代碼之間的代碼沒(méi)有對(duì) i 進(jìn)行過(guò)操作,它會(huì)自動(dòng)把上次讀的數(shù)據(jù)放在 b 中。而不是重新從 i 里面讀。這樣以來(lái),如果 i是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)就容易出錯(cuò),所以說(shuō) volatile 可以保證對(duì)特殊地址的穩(wěn)定訪問(wèn)。注意,在 VC 6 中,一般調(diào)試模式?jīng)]有進(jìn)行代碼優(yōu)化,所以這個(gè)關(guān)鍵字的作用看不出來(lái)。下面通過(guò)插入?yún)R編代碼,測(cè)試有無(wú) volatile 關(guān)鍵字,對(duì)程序最終代碼的影響,輸入下面的代碼:

        實(shí)例

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

        然后,在 Debug 版本模式運(yùn)行程序,輸出結(jié)果如下:

        i = 10  i = 32

        然后,在 Release 版本模式運(yùn)行程序,輸出結(jié)果如下:

        i = 10  i = 10

        輸出的結(jié)果明顯表明,Release 模式下,編譯器對(duì)代碼進(jìn)行了優(yōu)化,第二次沒(méi)有輸出正確的 i 值。下面,我們把 i 的聲明加上 volatile 關(guān)鍵字,看看有什么變化:

        實(shí)例

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

        分別在 Debug 和 Release 版本運(yùn)行程序,輸出都是:

        i = 10  i = 32

        這說(shuō)明這個(gè) volatile 關(guān)鍵字發(fā)揮了它的作用。其實(shí)不只是內(nèi)嵌匯編操縱棧”這種方式屬于編譯無(wú)法識(shí)別的變量改變,另外更多的可能是多線程并發(fā)訪問(wèn)共享變量時(shí),一個(gè)線程改變了變量的值,怎樣讓改變后的值對(duì)其它線程 visible。一般說(shuō)來(lái),volatile用在如下的幾個(gè)地方:

        • 1) 中斷服務(wù)程序中修改的供其它程序檢測(cè)的變量需要加 volatile;
        • 2) 多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加 volatile;
        • 3) 存儲(chǔ)器映射的硬件寄存器通常也要加 volatile 說(shuō)明,因?yàn)槊看螌?duì)它的讀寫(xiě)都可能由不同意義;

        2、volatile 指針

        和 const 修飾詞類似,const 有常量指針和指針常量的說(shuō)法,volatile 也有相應(yīng)的概念:

        修飾由指針指向的對(duì)象、數(shù)據(jù)是 const 或 volatile 的:

        const char* cpch;  volatile char* vpch;

        注意:對(duì)于 VC,這個(gè)特性實(shí)現(xiàn)在 VC 8 之后才是安全的。

        指針自身的值——一個(gè)代表地址的整數(shù)變量,是 const 或 volatile 的:

        char* const pchc;  char* volatile pchv;

        注意:

        • (1) 可以把一個(gè)非volatile int賦給volatile int,但是不能把非volatile對(duì)象賦給一個(gè)volatile對(duì)象。
        • (2) 除了基本類型外,對(duì)用戶定義類型也可以用volatile類型進(jìn)行修飾。
        • (3) C++中一個(gè)有volatile標(biāo)識(shí)符的類只能訪問(wèn)它接口的子集,一個(gè)由類的實(shí)現(xiàn)者控制的子集。用戶只能用const_cast來(lái)獲得對(duì)類型接口的完全訪問(wèn)。此外,volatile向const一樣會(huì)從類傳遞到它的成員。

        3、多線程下的volatile

        有些變量是用 volatile 關(guān)鍵字聲明的。當(dāng)兩個(gè)線程都要用到某一個(gè)變量且該變量的值會(huì)被改變時(shí),應(yīng)該用 volatile 聲明,該關(guān)鍵字的作用是防止優(yōu)化編譯器把變量從內(nèi)存裝入 CPU 寄存器中。如果變量被裝入寄存器,那么兩個(gè)線程有可能一個(gè)使用內(nèi)存中的變量,一個(gè)使用寄存器中的變量,這會(huì)造成程序的錯(cuò)誤執(zhí)行。volatile 的意思是讓編譯器每次操作該變量時(shí)一定要從內(nèi)存中真正取出,而不是使用已經(jīng)存在寄存器中的值,如下:

        volatile  BOOL  bStop  =  FALSE;

        (1) 在一個(gè)線程中:

        while(  !bStop  )  {  ...  }    bStop  =  FALSE;    return;    

        (2) 在另外一個(gè)線程中,要終止上面的線程循環(huán):

        bStop  =  TRUE;    while(  bStop  );  //等待上面的線程終止,如果bStop不使用volatile申明,那么這個(gè)循環(huán)將是一個(gè)死循環(huán),因?yàn)閎Stop已經(jīng)讀取到了寄存器中,寄存器中bStop的值永遠(yuǎn)不會(huì)變成FALSE,加上volatile,程序在執(zhí)行時(shí),每次均從內(nèi)存中讀出bStop的值,就不會(huì)死循環(huán)了。

        這個(gè)關(guān)鍵字是用來(lái)設(shè)定某個(gè)對(duì)象的存儲(chǔ)位置在內(nèi)存中,而不是寄存器中。因?yàn)橐话愕膶?duì)象編譯器可能會(huì)將其的拷貝放在寄存器中用以加快指令的執(zhí)行速度,例如下段代碼中:

        ...    int  nMyCounter  =  0;    for(;  nMyCounter<100;nMyCounter++)    {    ...    }    ...

        在此段代碼中,nMyCounter 的拷貝可能存放到某個(gè)寄存器中(循環(huán)中,對(duì) nMyCounter 的測(cè)試及操作總是對(duì)此寄存器中的值進(jìn)行),但是另外又有段代碼執(zhí)行了這樣的操作:nMyCounter -= 1; 這個(gè)操作中,對(duì) nMyCounter 的改變是對(duì)內(nèi)存中的 nMyCounter 進(jìn)行操作,于是出現(xiàn)了這樣一個(gè)現(xiàn)象:nMyCounter 的改變不同步。

        原文地址:https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777432.html

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)
        主站蜘蛛池模板: 国产精品v片在线观看不卡| 国产精品成人久久久久久久| 欧美日韩精品一区二区在线播放| 久久五月精品中文字幕| 欧美激情精品久久久久| 熟女精品视频一区二区三区| 久久精品中文字幕第23页| 91av国产精品| 国产福利微拍精品一区二区 | 国产综合色在线精品| 日韩经典精品无码一区| 国产精品九九九| 青青青国产依人精品视频| 国产AV无码专区亚洲精品| 久久国产精品无码一区二区三区 | 国产精品无码久久综合| 亚洲精品亚洲人成人网| 欧美ppypp精品一区二区| wwwvr高清亚洲精品二区| 久久久久夜夜夜精品国产| 国产91精品在线| 国产成人精品优优av| 久久精品aⅴ无码中文字字幕不卡| 尤物国精品午夜福利视频| 四虎国产精品永免费| 精品无码人妻一区二区三区| 亚洲av午夜福利精品一区| 亚洲高清专区日韩精品| 中文精品99久久国产| 野狼精品社区| 亚洲国产精品乱码一区二区| 亚洲av永久无码精品漫画| 人人妻人人澡人人爽人人精品97 | 99久久99久久精品国产片| 久久国产精品成人免费| 久久er国产精品免费观看2| 国产大片91精品免费观看不卡| 99re久久精品国产首页2020| 国产精品久久精品| 国产精品一久久香蕉产线看| 国产精品视频网|