Visual C++ >> Console
volatile
volatile是一個C語言的關鍵字,該關鍵字用來限制編譯器對變數做快取機制、最佳化的使用,在不考慮這個關鍵字前,如果變數比較常使用到時,則該變數就不會被更新到記憶體,等到最後再寫回記憶體即可,這樣是比較有效的運用快取機制,但是,卻會造成資料不同步的問題,尤其是在同步存取的時候,可能有另一個副程式取存到尚未更新完畢的記憶體資料,這就會導致不同步的問題,所以在變數名稱前加上volatile關鍵字就可以保證存取該變數時,CPU會做記憶體與快取資料的更新動作,達到正確存取資料的內容。
另外一個問題則是硬體初始化問題,對硬體初始化時,可能需要重覆送出相同的資料給硬體,但是最佳化時,就會變成只有執行一行送出資料的命令給硬體,因為編譯器只會對程式最佳化,編譯器不會考慮到硬體使用的時機問題,這個時候只要在變數前加上volatile關鍵字就可以告知編譯器不要對此變數做最佳化處理,達到送出相同命令給硬體的需求。
同步問題
如下例子,中斷副程式可能會不定時進入,但是,在main副程式卻是無窮迴圈的等待,所以運用快取機制時,bFlag可能不會被更新到記憶體,這樣會導致main副程式無法繼續運作
unsigned char bFlag=0; void Interrupt(void) interrupt 1 { bFlag = 0x00; } int main(int argc, char* argv[]) { bFlag = 0xff; while(bFlag); return 0; }
翻譯成組合語言
4: void Interrupt(void) interrupt 1 5: { 6: bFlag = 0; C:0x0007 750D00 MOV bFlag(0x0D),#0x00 7: } 5: int main(int argc, char* argv[]) C:0x009A 8E08 MOV 0x08,R6 C:0x009C 8F09 MOV 0x09,R7 C:0x009E 8B0A MOV 0x0A,R3 C:0x00A0 8A0B MOV 0x0B,R2 C:0x00A2 890C MOV 0x0C,R1 6: { 7: bFlag = 0xff; C:0x00A4 750DFF MOV bFlag(0x0D),#0xFF 8: while(bFlag); 9: C:0x00A7 E50D MOV A,bFlag(0x0D) C:0x00A9 70FC JNZ C:00A9 10: return 0; C:0x00AB E4 CLR A C:0x00AC FE MOV R6,A C:0x00AD FF MOV R7,A 12: }
從反組譯的結果可以看到C:0x00A9是一個無窮迴圈,也就是while(bFlag);這一行程式,但是,可以看到A暫存器沒有再度被更新,也就是沒有從記憶體更新到快取,所以,在中斷副程式改變的數值,將無法順利的被main副程式判斷到。
重複執行問題
如下例子,重複的程式碼在做完最佳化後,只會執行一行而已
#define CommandPort ((unsigned char *)0)[0xFF03] int main(int argc, char* argv[]) { *CommandPort = 0xaa; *CommandPort = 0xaa; *CommandPort = 0xaa; return 0; }
這個時候,只要在變數前加上volatile關鍵字,就可以讓這三行程式順利的被執行到。