微吼云上線多路互動直播服務 加速多場景互動直播落地
853
2025-03-31
總結
volatile 關鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統、硬件、其它線程等)更改。所以使用 volatile 告訴編譯器不應對這樣的對象進行優化。
volatile 關鍵字聲明的變量,每次訪問時都必須從內存中取出值(沒有被 volatile 修飾的變量,可能由于編譯器的優化,從 CPU 寄存器中取值)
const 可以是 volatile (如只讀的狀態寄存器)
指針可以是 volatile
1、volatile
被 volatile 修飾的變量,在對其進行讀寫操作時,會引發一些可觀測的副作用。而這些可觀測的副作用,是由程序之外的因素決定的。
2、volatile應用
(1)并行設備的硬件寄存器(如狀態寄存器)。 假設要對一個設備進行初始化,此設備的某一個寄存器為0xff800000
int *output = (unsigned int *)0xff800000; //定義一個IO端口; int init(void) { int i; for(i=0;i< 10;i++) { *output = i; } }
1
2
3
4
5
6
7
8
9
經過編譯器優化后,編譯器認為前面循環半天都是廢話,對最后的結果毫無影響,因為最終只是將output這個指針賦值為 9,所以編譯器最后給你編譯編譯的代碼結果相當于:
int init(void) { *output = 9; }
1
2
3
4
如果你對此外部設備進行初始化的過程是必須是像上面代碼一樣順序的對其賦值,顯然優化過程并不能達到目的。反之如果你不是對此端口反復寫操作,而是反復讀操作,其結果是一樣的,編譯器在優化后,也許你的代碼對此地址的讀操作只做了一次。然而從代碼角度看是沒有任何問題的。這時候就該使用volatile通知編譯器這個變量是一個不穩定的,在遇到此變量時候不要優化。
(2)一個中斷服務子程序中訪問到的變量
static int i=0; int main() { while(1) { if(i) dosomething(); } } /* Interrupt service routine */ void IRS() { i=1; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上面示例程序的本意是產生中斷時,由中斷服務子程序IRS響應中斷,變更程序變量i,使在main函數中調用dosomething函數,但是,由于編譯器判斷在main函數里面沒有修改過i,因此可能只執行一次對從i到某寄存器的讀操作,然后每次if判斷都只使用這個寄存器里面的“i副本”,導致dosomething永遠不會被調用。如果將變量i加上volatile修飾,則編譯器保證對變量i的讀寫操作都不會被優化,從而保證了變量i被外部程序更改后能及時在原程序中得到感知。
(3)多線程應用中被多個任務共享的變量。
當多個線程共享某一個變量時,該變量的值會被某一個線程更改,應該用 volatile 聲明。作用是防止編譯器優化把變量從內存裝入CPU寄存器中,當一個線程更改變量后,未及時同步到其它線程中導致程序出錯。volatile的意思是讓編譯器每次操作該變量時一定要從內存中真正取出,而不是使用已經存在寄存器中的值。示例如下:
volatile bool bStop=false; //bStop 為共享全局變量 //第一個線程 void threadFunc1() { ... while(!bStop){...} } //第二個線程終止上面的線程循環 void threadFunc2() { ... bStop = true; }
1
2
3
4
5
6
7
8
9
10
11
12
13
要想通過第二個線程終止第一個線程循環,如果bStop不使用volatile定義,那么這個循環將是一個死循環,因為bStop已經讀取到了寄存器中,寄存器中bStop的值永遠不會變成FALSE,加上volatile,程序在執行時,每次均從內存中讀出bStop的值,就不會死循環了。
是否了解volatile的應用場景是區分C/C++程序員和嵌入式開發程序員的有效辦法,搞嵌入式的家伙們經常同硬件、中斷、RTOS等等打交道,這些都要求用到volatile變量,不懂得volatile將會帶來程序設計的災難。(說的真對!)
3、volatile常見問題
下面的問題可以看一下面試者是不是直正了解volatile。
1)一個參數既可以是const還可以是volatile嗎?為什么?
可以。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2)一個指針可以是volatile嗎?為什么?
可以。盡管這并不常見。一個例子是當一個中斷服務子程序修該一個指向一個buffer的指針時。
3)下面的函數有什么錯誤?
int square(volatile int *ptr) { return *ptr * *ptr; }
1
2
3
4
這段代碼有點變態,其目的是用來返回指針ptr指向值的平方,但是,由于ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; }
1
2
3
4
5
6
7
由于*ptr的值可能被意想不到地改變,因此a和b可能是不同的。結果,這段代碼可能返回的不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr) { int a=*ptr; return a * a; }
1
2
3
4
5
4、volatile使用
#include
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local定義為:const volatile int local = 10;,再執行上述程序,輸出結果為:
Initial value of local : 10 Modified value of local: 100
1
2
C++ 單片機
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。