C語言中volatile關(guān)鍵字的作用及說明
volatile關(guān)鍵字的作用
在看C語言基礎(chǔ)知識的時候看到了volatile關(guān)鍵字,不是很理解,所以查了資料,總結(jié)一下。
volatile譯為:易變的
- volatile是一個類型修飾符(type specifier),就像我們熟悉的const一樣,它是被設(shè)計用來修飾被不同線程訪問和修改的變量;
- volatile的作用是作為指令關(guān)鍵字,確保本條指令不會因編譯器的優(yōu)化而省略,且要求每次直接讀值。
簡單地說就是防止編譯器對代碼進(jìn)行優(yōu)化。
比如如下程序:
XBYTE[2]=0x55; XBYTE[2]=0x56; XBYTE[2]=0x57; XBYTE[2]=0x58;
對外部硬件而言,上述四條語句分別表示不同的操作,會產(chǎn)生四種不同的動作,但是編譯器卻會對上述四條語句進(jìn)行優(yōu)化,認(rèn)為只有XBYTE[2]=0x58(即忽略前三條語句,只產(chǎn)生一條機器代碼)。
如果鍵入volatile,則編譯器會逐一地進(jìn)行編譯并產(chǎn)生相應(yīng)的機器代碼(產(chǎn)生四條代碼)。
典型的例子
for(int i=0; i<100000; i++);
這個語句用來測試空循環(huán)的速度的
但是編譯器肯定要把它優(yōu)化掉,根本就不執(zhí)行
如果你寫成
for(volatile int i=0; i<100000; i++);
它就會執(zhí)行了
volatile的本意是“易變的”
下面是volatile變量的幾個例子:
1)并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
2)一個中斷服務(wù)子程序中會訪問到的非自動變量(Non-automatic variables)
3)多線程應(yīng)用中被幾個任務(wù)共享的變量
一些相關(guān)的面試題
1)一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2)一個指針可以是volatile 嗎?解釋為什么。
3)下面的函數(shù)被用來計算某個整數(shù)的平方,它能實現(xiàn)預(yù)期設(shè)計目標(biāo)嗎?如果不能,試回答存在什么問題:
int square(volatile int *ptr) { return ((*ptr) * (*ptr)); }
下面是答案:
1)是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應(yīng)該試圖去修改它。
2)是的。盡管這并不很常見。一個例子是當(dāng)一個中斷服務(wù)子程序修改一個指向一個buffer的指針時。
3)這段代碼是個惡作劇。這段代碼的目的是用來返指針ptr指向值的平方,但是,由于ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int* &ptr)//這里參數(shù)應(yīng)該申明為引用,不然函數(shù)體里只會使用副本,外部沒法更改
{ int a,b; a = *ptr; b = *ptr; return a*b; }
由于ptr的值可能在兩次取值語句之間發(fā)生改變,因此a和b可能是不同的。
結(jié)果,這段代碼可能返回的不是你所期望的平方值!
正確的代碼如下:
long square(volatile intptr)
{ int a; a = *ptr; return a*a; }
另外,還可能出現(xiàn)在debug版中看不出來,在release版中才顯現(xiàn)的問題:
#include<stdio.h> void main(int argc,char *argv[]) { int i = 10; int a = i; printf("i=%d",a); //下面匯編語句的作用就是改變內(nèi)存中i的值,但是又不讓編譯器知道 __asm { mov dword ptr[ebp-4],20h } int b = i; printf("i=%d",b); }
然后,在調(diào)試版本模式運行程序,輸出結(jié)果如下:
i = 10
i = 32
然后,在release版本模式運行程序,輸出結(jié)果如下:
i = 10
i = 10
輸出的結(jié)果明顯表明,release模式下,編譯器對代碼進(jìn)行了優(yōu)化,第二次沒有輸出正確的i值。
我們把 i的聲明加上volatile關(guān)鍵字,看看有什么變化:
#include<stdio.h> void main(int argc,char *argv[]) { volatile int i = 10; int a = i; printf("i=%d",a); __asm { ` mov dword ptr[ebp-4],20h } int b = i; printf("i=%d",b); }
分別在調(diào)試版本和release版本運行程序,輸出都是:
i = 10
i = 32
這說明這個關(guān)鍵字發(fā)揮了它的作用!
volatile應(yīng)該解釋為“直接存取原始內(nèi)存地址”比較合適,“易變的”這種解釋簡直有點誤導(dǎo)人;
比如多線程的程序,共同訪問的內(nèi)存當(dāng)中,多個程序都可以操縱這個變量
你自己的程序,是無法判定何時這個變量會發(fā)生變化
還比如,他和一個外部設(shè)備的某個狀態(tài)對應(yīng),當(dāng)外部設(shè)備發(fā)生操作的時候,通過驅(qū)動程序和中斷事件,系統(tǒng)改變了這個變量的數(shù)值,而你的程序并不知道。
對于volatile類型的變量,系統(tǒng)每次用到他的時候都是直接從對應(yīng)的內(nèi)存當(dāng)中提取,而不會利用cache當(dāng)中的原有數(shù)值,以適應(yīng)它的未知何時會發(fā)生的變化,系統(tǒng)對這種變量的處理不會做優(yōu)化——顯然也是因為它的數(shù)值隨時都可能變化的情況。
使用地方
一般說來,volatile用在如下的幾個地方:
1、中斷服務(wù)程序中修改的供其它程序檢測的變量需要加volatile;
2、多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加volatile;
3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能有不同意義;
另外,以上這幾種情況經(jīng)常還要同時考慮數(shù)據(jù)的完整性(相互關(guān)聯(lián)的幾個標(biāo)志讀了一半被打斷了重寫),在1中可以通過關(guān)中斷來實現(xiàn),2 中可以禁止任務(wù)調(diào)度,3中則只能依靠硬件的良好設(shè)計了。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++面向?qū)ο蠖嗉壊藛蜗駻rduino的移植問題小結(jié)
這篇文章主要介紹了C++面向?qū)ο蠖嗉壊藛蜗駻rduino的移植問題及實現(xiàn)思路,本文通過示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-06-06C++實現(xiàn)LeetCode(25.每k個一組翻轉(zhuǎn)鏈表)
這篇文章主要介紹了C++實現(xiàn)LeetCode(25.每k個一組翻轉(zhuǎn)鏈表),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07c語言中位字段與結(jié)構(gòu)聯(lián)合的組合使用詳解
本篇文章是對c語言中位字段與結(jié)構(gòu)聯(lián)合的組合使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05