1. 程式人生 > >C++關鍵字的詳解 ---- volatile關鍵字

C++關鍵字的詳解 ---- volatile關鍵字

1.volatile的定義
volatile關鍵字是一種型別修飾符,用它宣告的型別變量表示可以被某些編譯器未知的因素更改,比如:作業系統、硬體或者其它執行緒等。由於訪問暫存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。遇到這個關鍵字宣告的變數,編譯器對訪問該變數的程式碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。
volatile的本意是“易變的”,不過翻譯成“直接存取原始記憶體地址”更為合適。“易變”是因為外在因素引起的,象多執行緒,中斷等,並不是因為用volatile修飾了的變數就是“易變”了,假如沒有外因,即使用volatile定義,它也不會變化。

2.volatile修飾變數的使用

    volatile int huangwen=10;
    int temp01 = huangwen;
    ...
    // 其他程式碼,並未明確告訴編譯器,對 huangwen 進行過操作
    int temp02 = i;

volatile 指出 huangwen 是隨時可能發生變化的,每次使用它的時候必須從 huangwen 的地址中讀取,因而編譯器生成的彙編程式碼會重新從 huangwen 的地址讀取資料放在 temp02 中。而優化做法是,由於編譯器發現兩次從 huangwen 讀資料的程式碼之間的程式碼沒有對 huangwen 進行過操作,它會自動把上次讀的資料放在 temp02 中。而不是重新從 huangwen 裡面讀。這樣以來,如果 huangwen 是一個暫存器變數或者表示一個埠資料就容易出錯,所以說 volatile 可以保證對特殊地址的穩定訪問。注意,在 VC 6.0 中,一般除錯模式沒有進行程式碼優化,所以這個關鍵字的作用看不出來。下面通過插入彙編程式碼,測試有無 volatile 關鍵字,對程式最終程式碼的影響:

    #include <stdio.h>
    void main()
    {
        int huangwen = 10;
        int temp01 = huangwen;
        printf("huangwen = %d", temp01);
        // 下面彙編語句的作用就是改變記憶體中 huangwen 的值
        // 但是又不讓編譯器知道
        __asm {
            mov dword ptr [ebp-4], 20h
            }
        int temp02 = huangwen;
        printf
("huangwen = %d", temp02); }

然後,在 Debug 版本模式執行程式,輸出結果如下:

huangwen = 10
huangwen = 32

然後,在 Release 版本模式執行程式,輸出結果如下:

huangwen = 10
huangwen = 10

輸出的結果明顯表明,Release 模式下,編譯器對程式碼進行了優化,第二次沒有輸出正確的 huangwen 值。下面,我們把 huangwen 的宣告加上 volatile 關鍵字,看看有什麼變化:

    #include <stdio.h>
    void main()
    {
        volatile int huangwen = 10;
        int temp01 = huangwen;
        printf("huangwen = %d", temp01);
        // 下面彙編語句的作用就是改變記憶體中 huangwen 的值
        // 但是又不讓編譯器知道
        __asm {
            mov dword ptr [ebp-4], 20h
            }
        int temp02 = huangwen;
        printf("huangwen = %d", temp02);
    }

然後,在 Debug 版本模式執行程式,輸出結果如下:

huangwen = 10
huangwen = 32

然後,在 Release 版本模式執行程式,輸出結果如下:

huangwen = 10
huangwen = 32

這說明這個 volatile 關鍵字發揮了它的作用。其實不只是“內嵌彙編操縱棧”這種方式屬於編譯無法識別的變數改變,另外更多的可能是多執行緒併發訪問共享變數時,一個執行緒改變了變數的值,怎樣讓改變後的值對其它執行緒 visible。一般說來,volatile用在如下的幾個地方:

  1. 中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
  2. 多工環境下各任務間共享的標誌應該加volatile;
  3. 儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;

3.volatile修飾指標的使用
和 const 修飾詞類似,const 有常量指標和指標常量的說法,volatile 也有相應的概念:

  • 修飾指向的物件、資料的指標是 const 或 volatile 的:
    const char* huangwenptr01;
    volatile char* huangwenptr02;

注意:對於 VC,這個特性實現在 VC 8 之後才是安全的。

  • 指標自身的值——一個代表地址的整數變數,是 const 或 volatile 的:
    char* const huangwenptr01;
    char* volatile huangwenptr02;

注意:

  • 可以把一個非volatile int賦給volatile int,但是不能把非volatile物件賦給一個volatile物件。
  • 除了基本型別外,對使用者定義型別也可以用volatile型別進行修飾。
  • C++中一個有volatile識別符號的類只能訪問它介面的子集,一個由類的實現者控制的子集。使用者只能用const_cast來獲得對型別介面的完全訪問。此外,volatile和const一樣會從類傳遞到它的成員。