C語言中volatile關鍵字

2021-06-18 17:12:15 字數 4317 閱讀 4293

1.編譯器優化介紹:

由於記憶體訪問速度遠不及cpu處理速度,為提高機器整體效能,在硬體上引入硬體快取記憶體cache,加速對記憶體的訪問。另外在現代cpu中指令的執行並不一定嚴格按照順序執行,沒有相關性的指令可以亂序執行,以充分利用cpu的指令流水線,提高執行速度。以上是硬體級別的優化。再看軟體一級的優化:一種是在編寫**時由程式設計師優化,另一種是由編譯器進行優化。編譯器優化常用的方法有:將記憶體變數快取到暫存器;調整指令順序充分利用cpu指令流水線,常見的是重新排序讀寫指令。對常規記憶體進行優化的時候,這些優化是透明的,而且效率很好。由編譯器優化或者硬體重新排序引起的問題的解決辦法是在從硬體(或者其他處理器)的角度看必須以特定順序執行的操作之間設定記憶體屏障(memory barrier),linux 提供了乙個巨集解決編譯器的執行順序問題。

void barrier(void)

這個函式通知編譯器插入乙個記憶體屏障,但對硬體無效,編譯後的**會把當前cpu暫存器中的所有修改過的數值存入記憶體,需要這些資料的時候再重新從記憶體中讀出。

2.volatile總是與優化有關,編譯器有一種技術叫做資料流分析,分析程式中的變數在**賦值、在**使用、在**失效,分析結果可以用於常量合併,常量傳播等優化,進一步可以消除一些**。但有時這些優化不是程式所需要的,這時可以用volatile關鍵字禁止做這些優化。

2.看兩個事例:

1>

告訴compiler不能做任何優化

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

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

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

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

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

int *ip = ...; 

*ip = 2; 

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

volatile int *ip = ...; 

*ip = 1; 

*ip = 2; 

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

2>

用volatile定義的變數會在程式外被改變,每次都必須從記憶體中讀取,而不能重複使用放在cache或暫存器中的備份。

例如:volatile char a;

a=0;

while(!a)

doother();

如果沒有 volatiledoother()不會被執行

3.下面是使用volatile變數的幾個場景:

1>中斷服務程式中修改的供其它程式檢測的變數需要加volatile;

例如: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也應該如此說明。

2>多工環境下各任務間共享的標誌應該加volatile

3>儲存器對映的硬體暫存器通常也要加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埠

另外,以上這幾種情況經常還要同時考慮資料的完整性(相互關聯的幾個標誌讀了一半被打斷了重寫),在1中可以通過關中斷來實現,2中禁止任務排程,3中則只能依靠硬體的良好設計。

4.幾個問題

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

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

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

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

5.volatile的本質:

1> 編譯器的優化

在本次執行緒內, 當讀取乙個變數時,為提高訪問速度,編譯器優化時有時會先把變數讀取到乙個暫存器中;以後,再取變數值時,就直接從暫存器中取值;當變數值在本執行緒裡改變時,會同時把變數的新值copy到該暫存器中,以便保持一致。

當變數在因別的執行緒等而改變了值,該暫存器的值不會相應改變,從而造成應用程式讀取的值和實際的變數值不一致。

當該暫存器在因別的執行緒等而改變了值,原變數的值不會改變,從而造成應用程式讀取的值和實際的變數值不一致。

2>volatile應該解釋為「直接訪問原始記憶體位址」比較合適,「易變的」這種解釋簡直有點誤導人。

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

int square(volatile int *ptr)

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

int square(volatile int *ptr)

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

long square(volatile int *ptr)

注意:頻繁地使用volatile很可能會增加**尺寸和降低效能,因此要合理的使用volatile。

1、c語言關鍵字volatile 

c語言關鍵字volatile表明某個變數的值可能在外部被改變,因此對這些變數的訪問不能快取到暫存器,每次使用時需要重新訪問。該關鍵字在多執行緒環境下經常使用,因為在編寫多執行緒的程式時,同乙個變數可能被多個執行緒修改,而程式通過該變數同步各個執行緒,例如: 

dword __stdcall threadfunc(lpvoid signal) 

該執行緒啟動時將intsignal 置為2,然後迴圈等待直到intsignal 為1 時退出。顯然intsignal的值必須在外部被改變,否則該執行緒不會退出。但是實際執行的時候該執行緒卻不會退出,即使在外部將它的值改為1,看一下對應的偽彙編**就明白了: 

mov ax,signal 

label: 

if(ax!=1) 

goto label 

對於c編譯器來說,它並不知道這個值會被其他執行緒修改。自然就把它cache在暫存器裡面。記住,c 編譯器是沒有執行緒概念的!這時候就需要用到volatile。volatile 的本意是指:這個值可能會在當前執行緒外部被改變。也就是說,我們要在threadfunc中的intsignal前面加上volatile關鍵字,這時候,編譯器知道該變數的值會在外部改變,因此每次訪問該變數時會重新讀取,所作的迴圈變為如下面偽碼所示: 

label: 

mov ax,signal 

if(ax!=1) 

goto label 

3、memory 

有了上面的知識就不難理解memory修改描述符了,memory描述符告知gcc: 

1)不要將該段內嵌彙編指令與前面的指令重新排序;也就是在執行內嵌彙編**之前,它前面的指令都執行完畢 

2)不要將變數快取到暫存器,因為這段**可能會用到記憶體變數,而這些記憶體變數會以不可預知的方式發生改變,因此gcc插入必要的**先將快取到暫存器的變數值寫回記憶體,如果後面又訪問這些變數,需要重新訪問記憶體。 

如果彙編指令修改了記憶體,但是gcc 本身卻察覺不到,因為在輸出部分沒有描述,此時就需要在修改描述部分增加「memory」,告訴gcc 記憶體已經被修改,gcc 得知這個資訊後,就會在這段指令之前,插入必要的指令將前面因為優化cache 到暫存器中的變數值先寫回記憶體,如果以後又要使用這些變數再重新讀取。 

使用「volatile」也可以達到這個目的,但是我們在每個變數前增加該關鍵字,不如使用「memory」方便。

c語言中volatile關鍵字

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

C語言中volatile關鍵字

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

c語言中的volatile關鍵字

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