volatile const int i;緩存
(1)本程序段中不能對 i 作修改,任何修改都是非法的,或者至少是粗心,編譯器應該報錯,防止這種粗心;
(2)另外一個程序段則徹底有可能修改,所以編譯器最好不要作太激進的優化。多線程
「const」的含義:「請作爲常量使用」,而並不是「放心吧,那確定是個常量」。
「volatile」的含義:「請不要作沒譜的優化,這個值可能變掉的」,而並不是「你能夠修改這個值」。
所以,它們原本就不是矛盾的。函數
const:優化
eg.1:spa
const意味着「只讀」,只讀表示編譯器不容許代碼修改變量,或者說這個變量在此處不能被修改,但並不表示這個變量在其它地方不可以被修改(不能被修改豈不就成了常量?)線程
#include <stdio.h>
int main(void) {
int i = 5;
const int* p = &i;
*p = 6; // 不能夠
i = 7; // 徹底能夠,並且那個「const」的「*p」也跟着變成了7。
printf("%d\n", *p);
return 0;
}
設計
eg.2:指針
void funcA(const char* str) {
...
}
在上面的程序中,str所指向的內存區域就是隻讀的,但這個只讀性只在函數funcA內部,
出了funcA,這塊內存徹底有多是可以被修改的。
void funcB(void) {
char name[] = "Jim King";
funcA(name);
}對象
以上兩例足夠說明,用const修飾表達了咱們本身的代碼不會改變所修飾的對象的值,可是別的代碼有可能改變此對象的值。內存
volatile:
volatile表示禁止優化。由於編譯器會認爲若是代碼沒有改變變量,那麼這個變量就不會改變,所以編譯器會用寄存器把該變量緩存起來,每次須要讀取變量值的時候,就從緩存中讀取。若是使用volatile,就應該直接從初始的內存區去讀數據。固然,每一次修改變量的值時,也是將新值寫入到內存中,而不是隻寫到緩存中。這在大多數時候是正確的,可是在多線程或者中斷的場合就不正確了。
eg.1:
中斷程序。在下面的程序中,咱們將intr_func註冊爲中斷函數,某個中斷髮生時觸發這一函數:
unsigned char flag = 1;
int main(int argc, char **argv) {
reg_intr(XXX, intr_func);
while(flag) {
printf("hello\n");
}
return 0;
}
void intr_func(void) {
flag = 0;
}
當不加volatile時,編譯器會直接將while條件中的flag換成1(即編譯器作了優化),所以即便中斷髮生也沒法結束循環;若是給flag加上volatile標識,編譯器就不會作前述的優化,程序就依照設計的意圖工做了。
const、volatile綜合舉例
eg.1:
嵌入式系統中比較常見的例子。不少嵌入式系統容許咱們訪問外部寄存器,該寄存器的地址假設是0x0018,並假設該寄存器的最低位表示設備狀態,1爲忙碌,0爲空閒。
#define GET_REG_VALUE(reg) (*reg) //get register value
const unsigned char* STATUS_REG = 0x0018; // status register
const unsigned char STATUS_BUSY = 0x01; //busy bit
while (GET_REG_VALUE(STATUS_REG) & STATUS_BUSY); // wait until free
// do something to operate the device
...
這段代碼極可能會死循環。由於編譯器會將地址0x0018的對象的值緩存起來,而後每次while的時候都從緩存中讀取。雖然STATUS_REG的值是const unsigned char *,但這僅僅表示STATUS_REG寄存器是個只讀寄存器,咱們不可以在代碼中去寫這個寄存器,但並不表示這個寄存器是不可以改變的。硬件完成了它的任務以後,就會把狀態設置成空閒,所以該寄存器的最後一位在咱們循環的時候極可能已經發生了變化。所以在這樣的地方,咱們要禁止編譯器自做聰明的優化,方法以下:
const volatile unsigned char *STATUS_REG = 0x0018; /* status register */
總結此例:
從const角度看,說明了用const修飾表達了咱們本身的代碼不會改變所修飾的對象的值,可是硬件有可能改變此對象的值。
從volatile角度看,因爲以前變量const修飾了變量,致使了此變量「只讀」。然而循環過程當中該變量又存在連續不變的狀況,編譯器就多管閒事的作了優化,致使後來寄存器發生變化也仍是死不要臉的進入循環,因此就加入volatile來告誡編譯器不要亂優化。
1.咱們須要明白「volatile」的含義並不是是「non-const」,因此他們才能夠放在一塊兒。
2.在C/C++語言中,const沒有反義詞,若是一個變量沒有const修飾,那它自己就是const的反義詞,而並不是加上volatile纔是const的反義詞。
3.volatile標識一個變量意味着這個變量可能被非本程序的其餘過程改變。若是一個變量不會被本程序改變,一般可能給它加上const,但若是該變量可能被其餘程序改變而本程序又在檢測這個變量的值,就須要給它加上volatile,因而變量就同時有volatile和const了。二者同時修飾一個對象的典型狀況,是用於驅動中訪問外部設備的只讀寄存器。
4.對於非指針非引用的變量,const volatile同時修飾的意義確實不大(我的以爲)。
const volatile int i=10;這行代碼有沒有問題?若是沒有,那 i 究竟是什麼屬性?
回答一:沒有問題,例如只讀的狀態寄存器。它是volatile,由於它可能被意想不到地改變;它是const,由於程序不該該試圖去修改它。volatile和const並不矛盾,只是控制的範圍不同,一個在程序自己以外,另外一個是程序自己。
回答二:沒問題,const和volatile這兩個類型限定符不矛盾。const表示(運行時)常量語義:被const修飾的對象在所在的做用域沒法進行修改操做,編譯器對於試圖直接修改const對象的表達式會產生編譯錯誤。volatile表示「易變的」,即在運行期對象可能在當前程序上下文的控制流之外被修改(例如多線程中被其它線程修改;對象所在的存儲器可能被多個硬件設備隨機修改等狀況):被volatile修飾的對象,編譯器不會對這個對象的操做進行優化。一個對象能夠同時被const和volatile修飾,代表這個對象體現常量語義,但同時可能被當前對象所在程序上下文意外的狀況修改。