新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 關于全局變量被修改以及volatile的用法

        關于全局變量被修改以及volatile的用法

        作者: 時間:2016-11-23 來源:網絡 收藏
        今天調一個程序的時候發現一個事情,就是全局變量在中斷中被改變,代碼如下

        本文引用地址:http://www.104case.com/article/201611/320154.htm

        .....

        unsigned char num=0;

        .....

        INTERRUPT()

        {

        ....

        num++;

        ....

        }

        void main()

        {

        out(num);

        }

        很不幸的事情是在主函數中,num一直都不會變,編譯器avrstdio,外部中斷。

        調試中發現中斷時可以進去的,然而中斷出來以后,這個全局變量就被改變了,后來加了volitale就可以了。。

        下面說說volatile的具體用法(以下內容都是摘抄):

        volatile的字面意思為“不穩定的,易變的”

        volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改。

        用volatile關鍵字聲明的變量i每一次被訪問時,執行部件都會從i相應的內存單元中取出i的值。

        沒有用volatile關鍵字聲明的變量i在被訪問的時候可能直接從cpu的寄存器中取值(因為之前i被訪問過,也就是說之前就從內存中取出i的值保存到某個寄存器中),之所以直接從寄存器中取值,而不去內存中取值,是因為編譯器優化代碼的結果(訪問cpu寄存器比訪問ram快的多)。

        以上兩種情況的區別在于被編譯成匯編代碼之后,兩者是不一樣的。之所以這樣做是因為變量i可能會經常變化,保證對特殊地址的穩定訪問。

        volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改,比如:操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

        使用該關鍵字的例子如下:
        volatile int i;
        當要求使用volatile聲明的變量的值的時候,系統總是重新從它所在的內存地址讀取數據,即使它前面的指令剛剛從該處讀取過數據,而且讀取的數據立刻被保存。

        例如:
        volatile int i = 10;
        int a = i;
        ...
        //其他代碼,并未明確告訴編譯器,對i進行過操作
        int b = i;

        volatile指出i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的匯編代碼會重新從i的地址讀取數據放在b中。而優化的做法是,由于編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中,而不是重新從i的地址中讀取。這樣一來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。

        注意,在VC6中,一般調試模式沒有進行代碼優化,所以這個關鍵字的作用看不出來。下面通過插入匯編代碼,測試有無volatile關鍵字,對程序最終代碼的影響:

        首先,用classwizard建一個win32 console工程,插入一個voltest.cpp文件,輸入下面的代碼:
         
        #include

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

        int b = i;
        printf("i = %d", b);
        }

        然后,在調試版本模式運行程序,輸出結果如下:
        i = 10
        i = 32

        然后,在release版本模式運行程序,輸出結果如下:
        i = 10
        i = 10

        輸出的結果明顯表明,release模式下,編譯器對代碼進行了優化,第二次沒有輸出正確的i值。

        下面,我們把i的聲明加上volatile關鍵字,看看有什么變化:

        #include

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

        int b = i;
        printf("i = %d", b);
        }

        分別在調試版本和release版本運行程序,輸出都是:
        i = 10
        i = 32

        這說明這個關鍵字發揮了它的作用!

        ================================

        將一個變量說明為volatile表示這個變量是“易變的”。如果一個變量會被其它引用改變,或在其它并行的任務中會被改變(例如中斷服務程序),都要顯式地說明為“volatile”,否則在編譯器優化階段會作出錯誤的判斷,例如將這個變量讀入寄存器以后,在沒有對這個變量賦值以前,會一直使用寄存器中的值,而實際上這個變量的值可能已經被一個指針引用改變了,或者是在中斷服務程序中被改變了,下面這個例子說明了這種錯誤:

        有一個變量T,在定時中斷中每隔一個固定時間減一,然后在主程序中等待它減到0。

        unsigned char T;
        void T0_int(void) interrupt 1 {
        ...
        T--;
        ...
        }

        void main(void)
        {
        ...
        T=10;
        while (T!=0);
        ...
        }

        正確的寫法應該是將第一句改為:
        volatile unsigned char T;

        從上面這個例子中很不幸的看到,在中斷中改變的這個數值如果不申請成volatile的話,被優化以后就麻煩了。。。

        這個在嵌入式環境中尤其重要。。好吧。。難。。以后要注意一下~~~~~

        2011.5.26新加:

        例子:串口通過中斷接受一個字節數據,然后在中斷中把這個數據存到一個緩沖區中,等待使用。當主程序中需要讀出數據時,就去讀緩沖區。此時緩沖區的聲明應該是

        volitale unsigned char rxbuf[SIZE]; 需要將關鍵字加上,否則緩沖區中讀出的數據可能不會對,第一個字節來的時候,再加一個延時,等待數據完成即可。



        關鍵詞: 全局變量volatil

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 哈密市| 安宁市| 玛纳斯县| 古田县| 宁南县| 搜索| 苗栗县| 乌拉特后旗| 临洮县| 张北县| 定西市| 若羌县| 大邑县| 安阳市| 翼城县| 伊通| 鹤壁市| 澜沧| 高安市| 兰州市| 蓝山县| 黔南| 宣城市| 东丽区| 桓台县| 额济纳旗| 普宁市| 邯郸县| 峡江县| 三原县| 化德县| 都昌县| 贺州市| 宣恩县| 铜梁县| 社旗县| 特克斯县| 德钦县| 连江县| 庄浪县| 潞西市|