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用在如下的幾個地方:
- 中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
- 多工環境下各任務間共享的標誌應該加volatile;
- 儲存器對映的硬體暫存器通常也要加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一樣會從類傳遞到它的成員。