Volatile 字面的意思時(shí)易變的,不穩定的。編譯器在優(yōu)化代碼時(shí),可能會(huì )把經(jīng)常用到的代碼存在Cache里面,然后下一次調用就直接讀取Cache而不是內存,這樣就大大提高了效率。但是問(wèn)題也隨之而來(lái)了。
本文引用地址:http://dyxdggzs.com/article/201611/322064.htm在多線(xiàn)程程序中,如果把一個(gè)變量放入Cache后,又有其他線(xiàn)程改變了變量的值,那么本線(xiàn)程是無(wú)法知道這個(gè)變化的。它可能會(huì )直接讀Cache里的數據。但是很不幸,Cache里的數據已經(jīng)過(guò)期了,讀出來(lái)的是不合時(shí)宜的臟數據。這時(shí)就會(huì )出現bug。也就是說(shuō)如果它是一個(gè)寄存器變量或者表示一個(gè)端口數據就容易出錯,所以說(shuō)volatile可以保證對特殊地址的穩定訪(fǎng)問(wèn)。
用Volatile聲明變量可以解決這個(gè)問(wèn)題。用Volatile聲明的變量就相當于告訴編譯器,我不要把這個(gè)變量寫(xiě)Cache,因為這個(gè)變量是可能發(fā)生改變的。
如果一個(gè)變量的值可能會(huì )被程序操作之外的其它操作所改變,那么你必需用volatile 聲明。在嵌入式系統中其它操作是:中斷服務(wù)程序的操作、硬件動(dòng)作的操作等,下面列舉了需要加volatile 聲明的情況。
1、中斷服務(wù)程序中修改的供其它程序檢測的變量需要加volatile;
2、多任務(wù)環(huán)境下各任務(wù)間共享的標志應該加volatile;
3、存儲器映射的硬件寄存器通常也要加volatile說(shuō)明,因為每次對它的讀寫(xiě)都可能由不同意義;
用volatile聲明的變量是不會(huì )被編譯器優(yōu)化掉的
例如:
#definePortA( * ( volatile unsignedint * )0x0000 )
這樣 PortA 成為一個(gè)地址在0x0000的unsigned int類(lèi)型變量。這個(gè)定義看起來(lái)很復雜,其實(shí)它也可以分解成幾個(gè)很簡(jiǎn)單的部分來(lái)看。 ( volatile unsignedint * )是C語(yǔ)言中的強制類(lèi)型轉換,它的作用是把0x0000這個(gè)純粹的十六進(jìn)制數轉換成為一個(gè)(地址)指針,其中volatile并不是必要的,它只是告訴編譯器,這個(gè)值與外界環(huán)境有關(guān),不要對它優(yōu)化。接下來(lái)在外面又加了一個(gè)*號,就表示0x0000內存單元中的內容了。經(jīng)過(guò)這個(gè)宏定義之后,PortA就被可以做為一個(gè)普通的變量來(lái)操作,所有出現PortA的地方編譯的時(shí)候都被替換成( * ( volatile unsignedint * )0x0000 ),外面一層括號是為了保證里面的操作不會(huì )因為運算符優(yōu)先級或者其它不可預測的原因被改變而無(wú)法得到預期的結果。
PORTA做為一個(gè)輸入端口,其值是由外部設備決定的,由于外部設備的變化是隨機的,因此第一次讀取的值和第二次讀取的值很可能不同,所以我們把它聲明為volatile變量。
a = PORTA;
a = PORTA;
由于PORTA是用volatile聲明的變量,編譯器不會(huì )把它優(yōu)化成一句,而如果不是volatile聲明的編譯器就會(huì )將第二句優(yōu)化掉,從而程序將會(huì )忽略輸入端口的變化。
通常把嵌入式設備的所有外圍器件寄存器都聲明為volatile的。
這種定義方法適合所有的C編譯器,可移植性好,但PortA并不是一個(gè)真正的變量,只是一個(gè)宏名,當你調試一個(gè)程序的時(shí)候,無(wú)法在調試窗口觀(guān)察它的值。另外連接器也失去了靈活性,它得防止其它變量跟此變量沖突。
評論