如果你懂一點點的編譯器的知識我想你都會知道編譯器在編譯你的代碼的時候,用進行自動優(yōu)化的,用以產(chǎn)生優(yōu)化指令。同上操作系統(tǒng)和一些線程同樣也會對你所定義的一些變量做出一些你所不知道的更改。這樣的更改我們稱為,隱式修改,因為你不知道,編譯器在什么情況下,在那里做出了優(yōu)化,甚至你都不知道,或是不能肯定編譯器到底有沒有對你的代碼做出優(yōu)化。
直接點把你看看下面的例子
#include <iostream>
void main()
{
int i=10;
int a = i;
printf("i= %d\n",a);
__asm {
mov dword ptr [ebp-4], 50h
}
//下面匯編語句的作用就是改變內(nèi)存中i的值,但是又不讓編譯器知道,來隱式的修改了變量。
int b = i;
printf("i= %d\n",b);
}
然后,在調(diào)試版本(debug)模式運行程序,輸出結果如下:
i = 10
i = 80
然后,在release版本模式運行程序,輸出結果如下:
i = 10
i = 10
呵呵結果看到了嗎?輸出的結果明顯表明,release模式下,編譯器對代碼進行了優(yōu)化,第二次沒有輸出正確的i值。所以得出一個結論在VC中release模式編譯代碼時編譯器會自動對你的代碼來做起優(yōu)化的。而調(diào)試版本(debug)模式下便不會。
廢話說了好多啊呵呵 下面繼續(xù)說說 volatile
下面,我們把 i的聲明加上volatile關鍵字,看看有什么效果:
#include <iostream>
void main()
{
volatile int i=10;
int a = i;
printf("i= %d\n",a);
__asm {
mov dword ptr [ebp-4], 50h
}
int b = i;
printf("i= %d\n",b);
}
這下你再在調(diào)試版本和release版本運行程序,看看輸出結果是不是都是:
i = 10
i = 32
估計大家看到這里便會明白了,volatile 這個關鍵字最最主要的意思是做什么的了。
在MSDN中volatile是一個限定符,也稱為keyword或描述符,"volatile 關鍵字指示字段可由操作系統(tǒng)、硬件或并發(fā)執(zhí)行的線程在程序中進行修改。"
當要求使用volatile 聲明的變量的值的時候,系統(tǒng)總是重新從它所在的內(nèi)存讀取數(shù)據(jù),即使它前面的指令剛剛從該處讀取過數(shù)據(jù)。而且讀取的數(shù)據(jù)立刻被保存。
一般說來,volatile用在如下的幾個地方:
1、中斷服務程序中修改的供其它程序檢測的變量需要加volatile;
2、多任務環(huán)境下各任務間共享的標志應該加volatile;
3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
聲明方式為 volatile declaration
備注
系統(tǒng)總是在 volatile 對象被請求的那一刻讀取其當前值,即使上一條指令從同一對象請求值。而且,該對象的值在賦值時立即寫入。
volatile 修飾符通常用于由多個線程訪問而不使用 lock 語句來序列化訪問的字段。使用 volatile 修飾符能夠確保一個線程檢索由另一線程寫入的最新值。
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1). 并行設備的硬件寄存器(如:狀態(tài)寄存器)
2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
3). 多線程應用中被幾個任務共享的變量
回答不出這個問題的人是不會被雇傭的。我認為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。嵌入式系統(tǒng)程序員經(jīng)常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內(nèi)容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
1). 一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2). 一個指針可以是volatile 嗎?解釋為什么。
3). 下面的函數(shù)有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2). 是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
=======================================================================================
簡單的理解是 volatile 對應著一個容易變化的東西,例如 IO 端口,加上該關鍵字可以阻止編譯器針對他做優(yōu)化動作。
compiler 做優(yōu)化的時候,遇到下面這樣的語句:
*p = 1;
*p = 2;
如果 p 指針沒有被明確指定是 volatile 的,就會認為賦值 1 再賦值 2 沒有必要,不如把 *p = 1; 省略,優(yōu)化成
*p = 2
如果希望向一個 I/O 端口寫 1 再寫 2,一定要明確指出 p 是 volatile 的。
遇到讀端口時也是一樣的情況,如果不寫 volatile,編譯器遇到下面的情況:
a = *p;
b = *p;
可能會優(yōu)化成下面這樣
t = *p
a = t
b = t
本應該讀兩次 I/O 端口,結果只讀了一次!
加上 volatile 后,一定能保證兩次都是去讀 I/O 端口。
a = *p
b = *p
======================================================================================
強制類型轉換
volatile 是一個類型修飾符號,表示變量的內(nèi)容在編譯器無法知道的情況下會發(fā)生變化,這種修飾類型作用下的變量的數(shù)值每次在使用的時候都從實際地址取,而不是從catch當中獲取
看這個代碼,可以去掉這個修飾符號這樣好理解
add是一個數(shù)字
(unsigned int *) add 可以認為是 add是一個無符號整數(shù)類型的指針
* (unsigned int *) add 可以認為是 add這個無符號整數(shù)類型指針指向地址的內(nèi)容了
加入volatile 的修飾表示的是,那個地址里面的數(shù)據(jù)是不確定情況下就可以改變的內(nèi)容