天天看點

詳解C中volatile關鍵字

volatile提醒編譯器它後面所定義的變量随時都有可能改變,是以編譯後的程式每次需要存儲或讀取這個變量的時候,都會直接從變量位址中讀取資料。如果沒有volatile關鍵字,則編譯器可能優化讀取和存儲,可能暫時使用寄存器中的值,如果這個變量由别的程式更新了的話,将出現不一緻的現象。下面舉例說明。在DSP開發中,經常需要等待某個事件的觸發,是以經常會寫出這樣的程式:

short flag;

void test()

{

do1();

while(flag==0);

do2();

}

    這段程式等待記憶體變量flag的值變為1(懷疑此處是0,有點疑問,)之後才運作do2()。變量flag的值由别的程式更改,這個程式可能是某個硬體中斷服務程式。例如:如果某個按鈕按下的話,就會對DSP産生中斷,在按鍵中斷程式中修改flag為1,這樣上面的程式就能夠得以繼續運作。但是,編譯器并不知道flag的值會被别的程式修改,是以在它進行優化的時候,可能會把flag的值先讀入某個寄存器,然後等待那個寄存器變為1。如果不幸進行了這樣的優化,那麼while循環就變成了死循環,因為寄存器的内容不可能被中斷服務程式修改。為了讓程式每次都讀取真正flag變量的值,就需要定義為如下形式:

volatile short flag;

    需要注意的是,沒有volatile也可能能正常運作,但是可能修改了編譯器的優化級别之後就又不能正常運作了。是以經常會出現debug版本正常,但是release版本卻不能正常的問題。是以為了安全起見,隻要是等待别的程式修改某個變量的話,就加上volatile關鍵字。

volatile的本意是“易變的”

      由于通路寄存器的速度要快過RAM,是以編譯器一般都會作減少存取外部RAM的優化。比如:

static int i=0;

int main(void)

...

while (1)

if (i) do_something();

/* Interrupt service routine. */

void ISR_2(void)

i=1;

    程式的本意是希望ISR_2中斷産生時,在main當中調用do_something函數,但是,由于編譯器判斷在main函數裡面沒有修改過i,是以可能隻執行一次對從i到某寄存器的讀操作,然後每次if判斷都隻使用這個寄存器裡面的“i副本”,導緻do_something永遠也不會被調用。如果變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優化(肯定執行)。此例中i也應該如此說明。

    一般說來,volatile用在如下的幾個地方:

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

2、多任務環境下各任務間共享的标志應該加volatile;

3、存儲器映射的硬體寄存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;

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

------------------越是喧嚣的世界,越需要甯靜的思考------------------

合抱之木,生于毫末;九層之台,起于壘土;千裡之行,始于足下。

積土成山,風雨興焉;積水成淵,蛟龍生焉;積善成德,而神明自得,聖心備焉。故不積跬步,無以至千裡;不積小流,無以成江海。骐骥一躍,不能十步;驽馬十駕,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓無爪牙之利,筋骨之強,上食埃土,下飲黃泉,用心一也。蟹六跪而二螯,非蛇鳝之穴無可寄托者,用心躁也。

繼續閱讀