本篇文章是對C語言中關鍵字volatile的含義進行了詳細的分析介紹,希望能在學習上幫助大家,

volatile是一個型別修飾符(type specifier),它是被設計用來修飾被不同執行緒訪問和修改的變數,如果沒有volatile,基本上會導致這樣的結果:要么無法撰寫多執行緒程式,要么編譯器失去大量優化的機會,
1.原理作用
Volatile意思是“易變的”,應該解釋為“直接存取原始記憶體地址”比較合適,“易變”是因為外在因素引起的,像多執行緒,中斷等,
C語言書籍這樣定義volatile關鍵字:
volatile提醒編譯器它后面所定義的變數隨時都有可能改變,因此編譯后的程式每次需要存盤或讀取這個變數的時候,告訴編譯器對該變數不做優化,都會直接從變數記憶體地址中讀取資料,從而可以提供對特殊地址的穩定訪問,
如果沒有volatile關鍵字,則編譯器可能優化讀取和存盤,可能暫時使用暫存器中的值,如果這個變數由別的程式更新了的話,將出現不一致的現象,(簡潔的說就是:volatile關鍵詞影響編譯器編譯的結果,用volatile宣告的變數表示該變數隨時可能發生變化,與該變數有關的運算,不要進行編譯優化,以免出錯)
2.一般用處
一般說來,volatile用在如下的幾個地方:
1)并行設備的硬體暫存器(如:狀態暫存器)
存盤器映射的硬體暫存器通常也要加 voliate,因為每次對它的讀寫都可能有不同意義,
例如:假設要對一個設備進行初始化,此設備的某一個暫存器為0xff800000,
int *output = (unsigned int *)0xff800000;//定義一個IO埠;
int init(void)
{
int i;
for(i=0;i< 10;i++){
*output = i;
}
}
經過編譯器優化后,編譯器認為前面回圈半天都是廢話,對最后的結果毫無影響,因為最終只是將output這個指標賦值為 9,所以編譯器最后給你編譯編譯的代碼結果相當于:
int init(void)
{
*output =9;
}
如果你對此外部設備進行初始化的程序是必須是像上面代碼一樣順序的對其賦值,顯然優化程序并不能達到目的,反之如果你不是對此埠反復寫操作,而是反復讀操作,其結果是一樣的,編譯器在優化后,也許你的代碼對此地址的讀操作只做了一次,然而從代碼角度看是沒有任何問題的,這時候就該使用volatile通知編譯器這個變數是一個不穩定的,在遇到此變數時候不要優化,
2)中斷服務程式中修改的供其它程式檢測的變數,需要加volatile;
當變數在觸發某中斷程式中修改,而編譯器判斷主函式里面沒有修改該變數,因此可能只執行一次從記憶體到某暫存器的讀操作,而后每次只會從該暫存器中讀取變數副本,使得中斷程式的操作被短路,
3)多任務環境下各任務間共享的標志,應該加volatile;
在本次執行緒內, 當讀取一個變數時,編譯器優化時有時會先把變數讀取到一個暫存器中;以后,再取變數值時,就直接從暫存器中取值;當記憶體變數或暫存器變數在因別的執行緒等而改變了值,該暫存器的值不會相應改變,從而造成應用程式讀取的值和實際的變數值不一致 ,
4)存盤器映射的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
假設要對一個設備進行初始化,此設備的某一個暫存器為0xff800000,for(i=0;i< 10;i++) *output = i;前面回圈半天都是廢話,對最后的結果毫無影響,因為最終只是將output這個指標賦值為9,省略了對該硬體IO埠反復讀的操作,
這是區分C程式員和嵌入式系統程式員的最基本的問題:嵌入式系統程式員經常同硬體、中斷、RTOS等等打交道,所有這些都要求使用volatile變數,不懂得volatile內容將會帶來災難,
3.volatile 問題和總結
volatile 常見的幾個面試題:
1)一個引數既可以是const還可以是volatile嗎?
可以的,例如只讀的狀態暫存器,它是volatile因為它可能被意想不到地改變,它是const因為程式不應該試圖去修改它,
2) 一個指標可以是volatile 嗎?
可以,當一個中服務子程式修改一個指向buffer的指標時,
4.下面的函式有什么錯誤?
int square(volatile int*ptr)
{
return*ptr * *ptr;
}
該程式的目的是用來返指標*ptr指向值的平方,但是,由于*ptr指向一個volatile型引數,編譯器將產生類似下面的代碼:
int square(volatile int*ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的,結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int*ptr)
{
int a;
a = *ptr;
return a * a;
}
注意:頻繁地使用volatile很可能會增加代碼尺寸和降低性能,因此要合理的使用volatile,
總結:
volatile 關鍵字是一種型別修飾符,用它宣告的型別變數表示可以被某些編譯器未知的因素更改,volatile 提醒編譯器它后面所定義的變數隨時都有可能改變,因此編譯后的程式每次需要存盤或讀取這個變數的時候,都會直接從變數地址中讀取資料,如 果沒有 volatile 關鍵字,則編譯器可能優化讀取和存盤,可能暫時使用暫存器中的值,如果這個變數由別的程式更新了的話,將出現不一致的現象,所以遇到這個關鍵字宣告的變數,編譯器對訪問該變數的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問,

如果你想更好的提升你的編程能力,學好C語言C++編程!彎道超車,快人一步!
【C語言C++學習企鵝圈子】,分享(原始碼、專案實戰視頻、專案筆記,基礎入門教程)
歡迎轉行和學習編程的伙伴,利用更多的資料學習成長比自己琢磨更快哦!
編程學習書籍:

編程學習視頻:

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/247918.html
標籤:C
