結論是用volatile宣告的變數,每次都會去ram裡面去取它真正的值。
沒用volatile宣告的變數,直接去cache裡取值,此時有可能ram裡面此變數真正的值已被更改,造成資料不同步。

-----以下是網路上找來的說明------
volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改。

用volatile關鍵字聲明的變量i每一次被訪問時,執行部件都會從i相應的內存單元中取出i的值。

沒 有用volatile關鍵字聲明的變量i在被訪問的時候可能直接從cpu的寄存器中取值(因為之前i被訪問過,也就是說之前就從內存中取出i的值保存到某 個寄存器中),之所以直接從寄存器中取值,而不去內存中取值,是因為編譯器優化代碼的結果(訪問cpu寄存器比訪問ram快的多)。

以上兩種情況的區別在於被編譯成彙編代碼之後,兩者是不一樣的。之所以這樣做是因為變量i可能會經常變化,保證對特殊地址的穩定訪問。

volatile關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素更改

,比如:操作系統、硬件或者其它線程等。遇到這個關鍵字聲明的變量,編譯器對訪問該變量的

代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。

使用該關鍵字的例子如下:
int volatile nVint;
  當要求使用volatile 聲明的變量的值的時候,系統總是重新從它所在的內存讀取數據,即

使它前面的指令剛剛從該處讀取過數據。而且讀取的數據立刻被保存。

例如:
volatile int i=10;
int a = i;
...
//其他代碼,並未明確告訴編譯器,對i進行過操作

int b = i;
  volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編

譯器生成的彙編代碼會重新從i的地址讀取數據放在b中。而優化做法是,由於編譯器發現兩次從

i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在b中。而不是重新

從i裏面讀。這樣以來,如果i是一個寄存器變量或者表示一個端口數據就容易出錯,所以說vola

tile可以保證對特殊地址的穩定訪問。
  注意,在vc6中,一般調試模式沒有進行代碼優化,所以這個關鍵字的作用看不出來。下麵

通過插入彙編代碼,測試有無volatile關鍵字,對程序最終代碼的影響:
  首先,用classwizard建一個win32 console工程,插入一個voltest.cpp文件,輸入下面的

代碼:
 
#include
void main()
{
int i=10;
int a = i;

printf("i= %d\n",a);
//下面彙編語句的作用就是改變內存中i的值,但是又不讓編譯器知道
__asm {
mov dword ptr [ebp-4], 20h
}

int b = i;
printf("i= %d\n",b);
}
然後,在調試版本模式運行程序,輸出結果如下:
i = 10
i = 32
然後,在release版本模式運行程序,輸出結果如下:
i = 10
i = 10
輸出的結果明顯表明,release模式下,編譯器對代碼進行了優化,第二次沒有輸出正確的i值。

下面,我們把 i的聲明加上volatile關鍵字,看看有什麼變化:
#include
void main()
{
volatile int i=10;
int a = i;

printf("i= %d\n",a);
__asm {
mov dword ptr [ebp-4], 20h
}

int b = i;
printf("i= %d\n",b);
}
分別在調試版本和release版本運行程序,輸出都是:
i = 10
i = 32
這說明這個關鍵字發揮了它的作用!

========================================

volatile的本意是一般有两种说法--1.“暂态的”;2.“易变的”。
这两种说法都有可行。但是究竟volatile是什么意思,现举例说明(以Keil-c与a51为例
例子来自Keil FQA),看完例子后你应该明白volatile的意思了,如果还不明白,那只好
再看一遍了。


例1.

void main (void)
{
volatile int i;
int j;

i = 1; //1 不被优化 i=1
i = 2; //2 不被优化 i=1
i = 3; //3 不被优化 i=1

j = 1; //4 被优化
j = 2; //5 被优化
j = 3; //6 j = 3
}
---------------------------------------------------------------------
例2.

函数:

void func (void)
{
unsigned char xdata xdata_junk;
unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

编译的汇编为:

0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A

000B F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000D 22 RET

将函数变为:
void func (void)
{
volatile unsigned char xdata xdata_junk;
volatile unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

编译的汇编为:
0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A a处

000B E0 MOVX A,@DPTR
000C F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

000E 22 RET


比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次
如在a处*p的内容有变化,则t2得到的则不是真正*p的内容。

---------------------------------------------------------------------
例3


volatile unsigned char bdata var; // use volatile keyword here
sbit var_0 = var^0;
sbit var_1 = var^1;
unsigned char xdata values[10];

void main (void) {
unsigned char i;

for (i = 0; i < sizeof (values); i++) {
var = values[i];
if (var_0) {
var_1 = 1; //a处

values[i] = var; // without the volatile keyword, the compiler
// assumes that 'var' is unmodified and does not
// reload the variable content.
}
}
}


在此例中,如在a处到下一句运行前,var如有变化则不会,如var=0xff; 则在
values[i] = var;得到的还是values[i] = 1;

---------------------------------------------------------------------
应用举例:

例1.
#define DBYTE ((unsigned char volatile data *) 0)

说明:此处不用volatile关键字,可能得不到真正的内容。
---------------------------------------------------------------------

例2.


#define TEST_VOLATILE_C

//***************************************************************
// verwendete Include Dateien
//***************************************************************
#if __C51__ < 600
#error: !! Keil 版本不正确
#endif

//***************************************************************
// 函数 void v_IntOccured(void)
//***************************************************************
extern void v_IntOccured(void);

//***************************************************************
// 变量定义
//***************************************************************
char xdata cvalue1; //全局xdata
char volatile xdata cvalue2; //全局xdata

//***************************************************************
// 函数: v_ExtInt0()
// 版本:
// 参数:
// 用途:cvalue1++,cvalue2++
//***************************************************************
void v_ExtInt0(void) interrupt 0 {
cvalue1++;
cvalue2++;
}

//***************************************************************
// 函数: main()
// 版本:
// 参数:
// 用途:测试volatile
//***************************************************************

void main() {
char cErg;

//1. 使cErg=cvalue1;
cErg = cvalue1;

//2. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
if (cvalue1 != cErg)
v_IntOccured();

//3. 使cErg=cvalue2;
cErg = cvalue2;

//4. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
if (cvalue2 != cErg)
v_IntOccured();

//5. 完成
while (1);
}

//***************************************************************
// 函数: v_IntOccured()
// 版本:
// 参数:
// 用途: 死循环
//***************************************************************
void v_IntOccured() {
while(1);
}


仿真可以看出,在没有用volatile时,即2处,程序不能进入v_IntOccured();
但在4处可以进入v_IntOccured();

volatile宣告 在C裡面有volatile這個宣告,通常是說這個變數會被外在routine改變, 在kernel裡面通常是指會被interrupt handler(有時就是硬體中斷的routine) 改變值,也就是被非同步的改變的變數。例如 unsigned long vloatile jiffies; jiffies在kernel是時間每次hardware的中斷會來改這個值 在asm裡面是說這個東西compiler時,gcc不要雞婆作optimized,因為 最佳化的結果,compiler會把code按照他想的方法放到記憶體裡, 但是有的code我們需要特定指定他一定要在某個記憶體上, 在kernel裡常有這樣情形發生,我們可以用 __asm__ __volatile__宣告一段assembly的code是不要做最佳化的。 例如cli sti #define disable() __asm__ __volatile__ ("cli"); #define enable() __asm__ __volatile__ ("sti");
volatile 為一關鍵字 加在變數的前面
被 volatile 宣告的變數 將不會使用最佳化編譯
有時一個變數的值改變了 compiler 並不會馬上將他寫入記憶體中
而會先把結果放在CPU暫存器中 等到處理結束之後 才寫入記憶體
若說這個變數是多執行緒的flag 其他的執行緒要透過這個變數來反應
而這個值卻又沒有寫入記憶體 這時便會發生意想不到的結果
又或者是這變數為一個硬體的暫存器 會被硬體所改變
然而compiler 並沒有正確的將值從硬體暫存器取出來
而是將自己暫存的值拿來使用
這種情況 就是要用volatile 來宣告變數 告訴compiler不要自己暫存變數來提升速度
如此這個變數有任何的改變 便會馬上反應出來
 


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 lver76 的頭像
    lver76

    Iver's Blog

    lver76 發表在 痞客邦 留言(0) 人氣()