<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

        C語言中關鍵字volatile追根問底

        作者:未知   來源:布冬冬 的空間   點擊數:  更新時間:2014年08月16日   【字體:

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

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

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

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮