C語言中volatile關鍵字

2021-06-06 05:20:18 字數 3112 閱讀 7255

一、前言——程式的優化

1)硬體級別上的優化

a.由於記憶體訪問速度遠不及cpu處理速度,為提高機器整體效能,在硬體上引入硬體快取記憶體cache,加速對記憶體的訪問。

b.在現代cpu中指令的執行並不一定嚴格按照順序執行,沒有相關性的指令可以亂序執行,以充分利用cpu的指令流水線,提高執行速度。

2)軟體級的優化

a.編寫**時由程式設計師優化

b.由編譯器進行優化。編譯器優化常用的方法有:將記憶體變數快取到暫存器;調整指令順序充分利用cpu指令流水線,常見的是重新排序讀寫指令。

二、volatile詳解

1.編譯器有一種技術叫做資料流分析,分析程式中的變數在**賦值、在**使用、在**失效,分析結果可以用於常量合併,常量傳播等優化,進一步可以消除一些**。但有時這些優化不是程式所需要的,這時可以用volatile關鍵字禁止做這些優化。volatile的本意是「易變的」 因為訪問暫存器要比訪問記憶體單元快的多,所以編譯器一般都會作減少訪問記憶體的優化,但有可能會讀髒資料。當要求使用volatile宣告變數值的時候,系統總是重新從它所在的記憶體讀取資料,即使它前面的指令剛剛從該處讀取過資料。

2.簡而言之,volatile變數的作用就兩個:

a、告訴編譯器不要進行優化。

比如:要往某一位址送兩指令:

int *ip =...; //裝置位址

*ip = 1; //第乙個指令

*ip = 2; //第二個指令

以上程式compiler可能做優化而成:

int *ip = ...;

*ip = 2;

結果第乙個指令丟失。如果用volatile, compiler就不允許做任何的優化,從而保證程式的原意:

volatile int *ip = ...;

*ip = 1;

*ip = 2;

即使你要compiler做優化,它也不會把兩次付值語句間化為一。它只能做其它的優化。

b、告訴系統始終從記憶體中取變數的值,而不是從快取中取變數的值。

比如:中斷服務程式中修改的供其它程式檢測的變數

static int i=0;

int main(void) }}

/* interrupt service routine. */

void isr_2(void)

程式的本意是希望isr_2中斷產生時,在main函式中呼叫dosomething函式,但是,由於編譯器判斷在main函式裡面沒有修改過i,因此可能只執行一次對從i到某暫存器的讀操作,然後每次if判斷都只使用這個暫存器裡面的「i副本」,導致dosomething永遠也不會被呼叫。如果將變數加上volatile修飾,則編譯器保證對此變數的讀寫操作都不會被優化(肯定執行)。此例中i也應該如此說明。

3.一般來說,volatile用在如下的地方:

a.中斷服務程式中修改的供其它程式檢測的變數要加volatile,如上面的例子。

b.多執行緒應用中被多個任務共享的變數要加volatile

如:a執行緒將變數複製入暫存器,然後進入迴圈,反覆檢測暫存器的值是否滿足一定條件(它期待b執行緒改變變數的值)在此種情況下,當b執行緒改變了變數的值時,改變的是其記憶體中的值,已改變的值對其在暫存器中的值沒有影響。所以a執行緒進入死迴圈。此時必須使用volatile。

c.並行裝置的硬體暫存器(如:狀態暫存器)

d.儲存器對映的硬體暫存器通常也要加voliate,因為每次對它的讀寫都可能有不同意義。

如:假設要對乙個裝置進行初始化,此裝置的某乙個暫存器為0xff800000。

int  *output = (unsigned  int *)0xff800000;//定義乙個io埠;

int init(void)

}

經過編譯器優化後,編譯器認為前面迴圈半天都是廢話,對最後的結果毫無影響,因為最終只是將output這個指標賦值為9,所以編譯器最後給你編譯編譯的**結果相當於:

int  init(void)

如果你對此外部裝置進行初始化的過程是必須是像上面**一樣順序的對其賦值,顯然優化過程並不能達到目的。反之如果你不是對此埠反覆寫操作,而是反覆讀操作,其結果是一樣的,編譯器在優化後,也許你的**對此位址的讀操作只做了一次。然而從**角度看是沒有任何問題的。這時候就該使用volatile通知編譯器這個變數是乙個不穩定的,在遇到此變數時候不要優化。

例如:volatile  int *output=(volatile unsigned int *)0xff800000;//定義乙個i/o埠

4.幾個問題

1).乙個引數既可以是const還可以是volatile嗎?

可以的,例如唯讀的狀態暫存器。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。

2).乙個指標可以是volatile 嗎?

可以,當乙個中服務子程式修該乙個指向乙個buffer的指標時。

3).下面的函式有什麼錯誤:

int square(volatile int *ptr)

該程式的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向乙個volatile型引數,編譯器將產生類似下面的**:

int square(volatile int *ptr)

由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段**可能返不是你所期望的平方值!正確的**如下:

long square(volatile int *ptr)

注意:頻繁地使用volatile很可能會增加**尺寸和降低效能,因此要合理的使用volatile。因為volatile抑制了優化,因此應儘量減少對它的引用操作,最好只是對其進行簡單的賦值。如:

volatile char *pcwr_g;

...while(...)

*pcwr_g ++=udr;

...考慮改寫成:

volatile char *pcwr_g;

char *pctemp;

...pctemp = pcwr_g;

while(...)

*pctemp++=udr;

pcwr_g = pctemp;

...一般而言,這樣改寫後效率更高些,可以在迴圈中只針對暫存器操作。

c語言中volatile關鍵字

volatile關鍵字是一種型別修飾符,用它宣告的型別變數表示可以被某些編譯器未知的因素更改 比如 作業系統 硬體或者其它執行緒等。遇到這個關鍵字宣告的變數,編譯器對訪問該變數的 就不再進行優化,從而可以提供對特殊位址的穩定訪問。使用該關鍵字的例子如下 int volatile nvint 當要求使...

C語言中volatile關鍵字

1.編譯器優化介紹 由於記憶體訪問速度遠不及cpu處理速度,為提高機器整體效能,在硬體上引入硬體快取記憶體cache,加速對記憶體的訪問。另外在現代cpu中指令的執行並不一定嚴格按照順序執行,沒有相關性的指令可以亂序執行,以充分利用cpu的指令流水線,提高執行速度。以上是硬體級別的優化。再看軟體一級...

c語言中的volatile關鍵字

volatile關鍵字是一種型別修飾符,用它宣告的型別變數表示可以被某些編譯器未知的因素更改。用volatile關鍵字宣告的變數i每一次被訪問時,執行部件都會從i相應的記憶體單元中取出i的值。沒有用volatile關鍵字宣告的變數i在被訪問的時候可能直接從cpu的暫存器中取值 因為之前i被訪問過,也...