C語言丨深刻理解volatile關鍵字

本篇文章是對C語言中關鍵字volatile的含義進行了詳細的分析介紹,但願能在學習上幫助你們。程序員


 

volatile是一個類型修飾符(type specifier)。它是被設計用來修飾被不一樣線程訪問和修改的變量。若是沒有volatile,基本上會致使這樣的結果:要麼沒法編寫多線程程序,要麼編譯器失去大量優化的機會。面試

1.原理做用

Volatile意思是「易變的」,應該解釋爲「直接存取原始內存地址」比較合適。「易變」是由於外在因素引發的,像多線程,中斷等。編程

C語言書籍這樣定義volatile關鍵字:多線程

volatile提醒編譯器它後面所定義的變量隨時都有可能改變,所以編譯後的程序每次須要存儲或讀取這個變量的時候,告訴編譯器對該變量不作優化,都會直接從變量內存地址中讀取數據,從而能夠提供對特殊地址的穩定訪問。函數

若是沒有volatile關鍵字,則編譯器可能優化讀取和存儲,可能暫時使用寄存器中的值,若是這個變量由別的程序更新了的話,將出現不一致的現象。(簡潔的說就是:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發生變化,與該變量有關的運算,不要進行編譯優化,以避免出錯)性能

2.通常用處

通常說來,volatile用在以下的幾個地方: 學習

1)並行設備的硬件寄存器(如:狀態寄存器)

         存儲器映射的硬件寄存器一般也要加 voliate,由於每次對它的讀寫均可能有不一樣意義。優化

        例如:假設要對一個設備進行初始化,此設備的某一個寄存器爲0xff800000。spa

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++學習企鵝圈子】,分享(源碼、項目實戰視頻、項目筆記,基礎入門教程)
歡迎轉行和學習編程的夥伴,利用更多的資料學習成長比本身琢磨更快哦!

編程學習書籍:


 

編程學習視頻:

相關文章
相關標籤/搜索