當前位置:首頁 » 編程語言 » c語言volatile關鍵字

c語言volatile關鍵字

發布時間: 2022-05-16 15:11:01

c語言 中volatile關鍵字的用法

volatile是個多線程變數,就是可能在很多函數中會修改他。特別是中斷函數中一定要用他。volatile的意思就是「不穩定的」可能突發地改變它,單片機每次調用他的話,都要從他的地址取數。預防單片機不知道普通的變數改變了,還是用上一次的數據。
定義方法是 :
volatile char i; //定義一個字元型的多線程變數"i"
volatile int j; //定義一個整型的多線程變數"j"

記得給我打個辛苦分!哈哈

❷ c語言中volatile關鍵字是什麼含義怎麼辦呢

volatile
影響編譯器編譯的結果,指出,volatile
變數是隨時可能發生變化的,與volatile變數有關的運算,不要進行編譯優化,以免出錯,(vc++
在產生release版可執行碼時會進行編譯優化,加volatile關鍵字的變數有關的運算,將不進行編譯優化。)。
例如:
volatile
int
i=10;
int
j
=
i;
...
int
k
=
i;
volatile
告訴編譯器i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的可執行碼會重新從i的地址讀取數據放在k中。
而優化做法是,由於編譯器發現兩次從i讀數據的代碼之間的代碼沒有對i進行過操作,它會自動把上次讀的數據放在k中。而不是重新從i裡面讀。這樣以來,如果i是一個寄存器變數或者表示一個埠數據就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問,不會出錯。

❸ C語言中Valatile關鍵字有什麼用

volatile提醒編譯器它後面所定義的變數隨時都有可能改變,因此編譯後的程序每次需要存儲或讀取這個變數的時候,都會直接從變數地址中讀取數據。如果沒有volatile關鍵字,則編譯器可能優化讀取和存儲,可能暫時使用寄存器中的值,如果這個變數由別的程序更新了的話,將出現不一致的現象。下面舉例說明。在DSP開發中,經常需要等待某個事件的觸發,所以經常會寫出這樣的程序:
short flag;
void test()
{
do1();
while(flag==0);
do2();
}
這段程序等待內存變數flag的值變為1(懷疑此處是0,有點疑問,)之後才運行do2()。變數flag的值由別的程序更改,這個程序可能是某個硬體中斷服務程序。例如:如果某個按鈕按下的話,就會對DSP產生中斷,在按鍵中斷程序中修改flag為1,這樣上面的程序就能夠得以繼續運行。但是,編譯器並不知道flag的值會被別的程序修改,因此在它進行優化的時候,可能會把flag的值先讀入某個寄存器,然後等待那個寄存器變為1。如果不幸進行了這樣的優化,那麼while循環就變成了死循環,因為寄存器的內容不可能被中斷服務程序修改。為了讓程序每次都讀取真正flag變數的值,就需要定義為如下形式:
volatile short flag;
需要注意的是,沒有volatile也可能能正常運行,但是可能修改了編譯器的優化級別之後就又不能正常運行了。因此經常會出現debug版本正常,但是release版本卻不能正常的問題。所以為了安全起見,只要是等待別的程序修改某個變數的話,就加上volatile關鍵字。

volatile的本意是「易變的」
由於訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優化。比如:
static int i=0;
int main(void)
{
...
while (1)
{
if (i) do_something();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中斷產生時,在main當中調用do_something函數,但是,由於編譯器判斷在main函數裡面沒有修改過i,因此可能只執行一次對從i到某寄存器的讀操作,然後每次if判斷都只使用這個寄存器裡面的「i副本」,導致do_something永遠也不會被調用。如果變數加上volatile修飾,則編譯器保證對此變數的讀寫操作都不會被優化(肯定執行)。此例中i也應該如此說明。
一般說來,volatile用在如下的幾個地方:
1、中斷服務程序中修改的供其它程序檢測的變數需要加volatile;
2、多任務環境下各任務間共享的標志應該加volatile;
3、存儲器映射的硬體寄存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
另外,以上這幾種情況經常還要同時考慮數據的完整性(相互關聯的幾個標志讀了一半被打斷了重寫),在1中可以通過關中斷來實現,2中可以禁止任務調度,3中則只能依靠硬體的良好設計了。
二、volatile 的含義
volatile總是與優化有關,編譯器有一種技術叫做數據流分析,分析程序中的變數在哪裡賦值、在哪裡使用、在哪裡失效,分析結果可以用於常量合並,常量傳播等優化,進一步可以死代碼消除。但有時這些優化不是程序所需要的,這時可以用volatile關鍵字禁止做這些優化,volatile的字面含義是易變的,它有下面的作用:
1 不會在兩個操作之間把volatile變數緩存在寄存器中。在多任務、中斷、甚至setjmp環境下,變數可能被其他的程序改變,編譯器自己無法知道,volatile就是告訴編譯器這種情況。
2 不做常量合並、常量傳播等優化,所以像下面的代碼:
volatile int i = 1;
if (i > 0) ...
if的條件不會當作無條件真。
3 對volatile變數的讀寫不會被優化掉。如果你對一個變數賦值但後面沒用到,編譯器常常可以省略那個賦值操作,然而對Memory Mapped IO的處理是不能這樣優化的。
前面有人說volatile可以保證對內存操作的原子性,這種說法不大准確,其一,x86需要LOCK前綴才能在SMP下保證原子性,其二,RISC根本不能對內存直接運算,要保證原子性得用別的方法,如atomic_inc。
對於jiffies,它已經聲明為volatile變數,我認為直接用jiffies++就可以了,沒必要用那種復雜的形式,因為那樣也不能保證原子性。
你可能不知道在Pentium及後續CPU中,下面兩組指令
inc jiffies
;;
mov jiffies, %eax
inc %eax
mov %eax, jiffies
作用相同,但一條指令反而不如三條指令快。
三、編譯器優化 → C關鍵字volatile → memory破壞描述符zz
「memory」比較特殊,可能是內嵌匯編中最難懂部分。為解釋清楚它,先介紹一下編譯器的優化知識,再看C關鍵字volatile。最後去看該描述符。
1、編譯器優化介紹
內存訪問速度遠不及CPU處理速度,為提高機器整體性能,在硬體上引入硬體高速緩存Cache,加速對內存的訪問。另外在現代CPU中指令的執行並不一定嚴格按照順序執行,沒有相關性的指令可以亂序執行,以充分利用CPU的指令流水線,提高執行速度。以上是硬體級別的優化。再看軟體一級的優化:一種是在編寫代碼時由程序員優化,另一種是由編譯器進行優化。編譯器優化常用的方法有:將內存變數緩存到寄存器;調整指令順序充分利用CPU指令流水線,常見的是重新排序讀寫指令。對常規內存進行優化的時候,這些優化是透明的,而且效率很好。由編譯器優化或者硬體重新排序引起的問題的解決辦法是在從硬體(或者其他處理器)的角度看必須以特定順序執行的操作之間設置內存屏障(memory barrier),linux 提供了一個宏解決編譯器的執行順序問題。
void Barrier(void)
這個函數通知編譯器插入一個內存屏障,但對硬體無效,編譯後的代碼會把當前CPU寄存器中的所有修改過的數值存入內存,需要這些數據的時候再重新從內存中讀出。
2、C語言關鍵字volatile
C語言關鍵字volatile(注意它是用來修飾變數而不是上面介紹的__volatile__)表明某個變數的值可能在外部被改變,因此對這些變數的存取不能緩存到寄存器,每次使用時需要重新存取。該關鍵字在多線程環境下經常使用,因為在編寫多線程的程序時,同一個變數可能被多個線程修改,而程序通過該變數同步各個線程,例如:
DWORD __stdcall threadFunc(LPVOID signal)
{
int* intSignal=reinterpret_cast<int*>(signal);
*intSignal=2;
while(*intSignal!=1)
sleep(1000);
return 0;
}
該線程啟動時將intSignal 置為2,然後循環等待直到intSignal 為1 時退出。顯然intSignal的值必須在外部被改變,否則該線程不會退出。但是實際運行的時候該線程卻不會退出,即使在外部將它的值改為1,看一下對應的偽匯編代碼就明白了:
mov ax,signal
label:
if(ax!=1)
goto label
對於C編譯器來說,它並不知道這個值會被其他線程修改。自然就把它cache在寄存器裡面。記住,C 編譯器是沒有線程概念的!這時候就需要用到volatile。volatile 的本意是指:這個值可能會在當前線程外部被改變。也就是說,我們要在threadFunc中的intSignal前面加上volatile關鍵字,這時候,編譯器知道該變數的值會在外部改變,因此每次訪問該變數時會重新讀取,所作的循環變為如下面偽碼所示:
label:
mov ax,signal
if(ax!=1)
goto label
3、Memory
有了上面的知識就不難理解Memory修改描述符了,Memory描述符告知GCC:
1)不要將該段內嵌匯編指令與前面的指令重新排序;也就是在執行內嵌匯編代碼之前,它前面的指令都執行完畢
2)不要將變數緩存到寄存器,因為這段代碼可能會用到內存變數,而這些內存變數會以不可預知的方式發生改變,因此GCC插入必要的代碼先將緩存到寄存器的變數值寫回內存,如果後面又訪問這些變數,需要重新訪問內存。
如果匯編指令修改了內存,但是GCC 本身卻察覺不到,因為在輸出部分沒有描述,此時就需要在修改描述部分增加「memory」,告訴GCC 內存已經被修改,GCC 得知這個信息後,就會在這段指令之前,插入必要的指令將前面因為優化Cache 到寄存器中的變數值先寫回內存,如果以後又要使用這些變數再重新讀取。
使用「volatile」也可以達到這個目的,但是我們在每個變數前增加該關鍵字,不如使用「memory」方便。

❹ C語言中的volatile是什麼意思

volatile 實例講解

volatile的本意是一般有兩種說法--1.「暫態的」;2.「易變的」。
這兩種說法都有可行。但是究竟volatile是什麼意思,現舉例說明(以Keil-c與a51為例
例子來自Keil FQA),看完例子後你應該明白volatile的意思了,如果還不明白,那隻好
再看一遍了。

例1.

void main (void)
{
volatile int i;
int j;

i = 1; //1 不被優化 i=1
i = 2; //2 不被優化 i=1
i = 3; //3 不被優化 i=1

j = 1; //4 被優化
j = 2; //5 被優化
j = 3; //6 j = 3
}
---------------------------------------------------------------------
例2.

函數:

void func (void)
{
unsigned char xdata xdata_junk;
unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

編譯的匯編為:

0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A

000B F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
000D 22 RET

將函數變為:
void func (void)
{
volatile unsigned char xdata xdata_junk;
volatile unsigned char xdata *p = &xdata_junk;
unsigned char t1, t2;

t1 = *p;
t2 = *p;
}

編譯的匯編為:
0000 7E00 R MOV R6,#HIGH xdata_junk
0002 7F00 R MOV R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82 MOV DPL,R7
0006 8E83 MOV DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
0008 E0 MOVX A,@DPTR
0009 F500 R MOV t1,A a處

000B E0 MOVX A,@DPTR
000C F500 R MOV t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

000E 22 RET

比較結果可以看出來,未用volatile關鍵字時,只從*p所指的地址讀一次
如在a處*p的內容有變化,則t2得到的則不是真正*p的內容。

---------------------------------------------------------------------
例3

volatile unsigned char bdata var; // use volatile keyword here
sbit var_0 = var^0;
sbit var_1 = var^1;
unsigned char xdata values[10];

void main (void) {
unsigned char i;

for (i = 0; i < sizeof (values); i++) {
var = values[i];
if (var_0) {
var_1 = 1; //a處

values[i] = var; // without the volatile keyword, the compiler
// assumes that 'var' is unmodified and does not
// reload the variable content.
}
}
}

在此例中,如在a處到下一句運行前,var如有變化則不會,如var=0xff; 則在
values[i] = var;得到的還是values[i] = 1;

---------------------------------------------------------------------
應用舉例:

例1.
#define DBYTE ((unsigned char volatile data *) 0)

說明:此處不用volatile關鍵字,可能得不到真正的內容。
---------------------------------------------------------------------

例2.

#define TEST_VOLATILE_C

//***************************************************************
// verwendete Include Dateien
//***************************************************************
#if __C51__ < 600
#error: !! Keil 版本不正確
#endif

//***************************************************************
// 函數 void v_IntOccured(void)
//***************************************************************
extern void v_IntOccured(void);

//***************************************************************
// 變數定義
//***************************************************************
char xdata cvalue1; //全局xdata
char volatile xdata cvalue2; //全局xdata

//***************************************************************
// 函數: v_ExtInt0()
// 版本:
// 參數:
// 用途:cvalue1++,cvalue2++
//***************************************************************
void v_ExtInt0(void) interrupt 0 {
cvalue1++;
cvalue2++;
}

//***************************************************************
// 函數: main()
// 版本:
// 參數:
// 用途:測試volatile
//***************************************************************

void main() {
char cErg;

//1. 使cErg=cvalue1;
cErg = cvalue1;

//2. 在此處模擬時手動產生中斷INT0,使cvalue1++; cvalue2++
if (cvalue1 != cErg)
v_IntOccured();

//3. 使cErg=cvalue2;
cErg = cvalue2;

//4. 在此處模擬時手動產生中斷INT0,使cvalue1++; cvalue2++
if (cvalue2 != cErg)
v_IntOccured();

//5. 完成
while (1);
}

//***************************************************************
// 函數: v_IntOccured()
// 版本:
// 參數:
// 用途: 死循環
//***************************************************************
void v_IntOccured() {
while(1);
}

模擬可以看出,在沒有用volatile時,即2處,程序不能進入v_IntOccured();
但在4處可以進入v_IntOccured();

❺ c語言的關鍵字有哪些

C語言的關鍵字共有32個,根據關鍵字的作用,可分其為四類:
數據類型關鍵字(12個);

char:聲明字元型變數或函數
int: 聲明整型變數或函數
short:聲明短整型變數或函數
long:聲明長整型變數或函數
float:聲明浮點型變數或函數
double:聲明雙精度變數或函數
unsigned:聲明無符號類型變數或函數
signed:聲明有符號類型變數或函數
enum:聲明枚舉類型
struct:聲明結構體變數或函數
union:聲明共用體(聯合)數據類型
void :聲明函數無返回值或無參數,聲明無類型指針
控制語句關鍵字(12個);

循環語句
for:一種循環語句
do:循環語句的循環體
while:循環語句的循環條件
continue:結束當前循環,開始下一輪循環
break:跳出當前循環
條件語句
if: 條件語句
else :條件語句否定分支(與 if 連用)
goto:無條件跳轉語句
開關語句
switch:用於開關語句
case:開關語句分支
default:開關語句的「其他」分支
返回語句
return:子程序返回語句(可以帶參數,也看不帶參數)
存儲類型關鍵字(4個);
extern:聲明變數在其他文件中聲明
static :聲明靜態變數
register:聲明寄存器變數
auto :聲明自動變數,一般不使用
其它關鍵字(4個);
const :聲明只讀變數
sizeof:計算數據類型長度
typedef:給數據類型取別名
volatile:說明變數在程序執行中可被隱含地改變。

❻ C語言中關鍵字volatile是什麼意思

volatile的本意是一般有兩種說法。1.「暫態的 2.「易變的。這兩種說法都有可行。一個定義為volatile的變數是說這變數可能會被意想不到的改變,這樣,編譯器就不會去假設這個變數的值了。優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用保存在寄存器里的備份。 www.okeycar.com

❼ C語言中volatile關鍵字的用法是什麼

volatile
int
i
=
10;
int
k
=
i;
這段代碼如果沒有加上volatile,它的執行過程是
i
=
10,
10
->reg寄存器,k
=
reg值,就是說,k=i實際上是從寄存器里讀出來的。這樣快捷高效,當然並不是每次都這樣,因為寄存器有限。如果在10->reg寄存器的時候,i存儲的內存變化了,也就是別的線程修改了i的值,k=i仍然會按照i=10來執行
加上volatile後,執行過程是
i
=
10,
k
=
*(int
*)(&i);
它會從i的內存地址上去讀,當然值會非常准確,但是效率會比從寄存器中讀要慢很多。

❽ c語言中volatile關鍵字是什麼含義

用volatile關鍵字定義變數,相當於告訴編譯器,這個變數的值會隨時發生變化,每次使用時都需要去內存里重新讀取它的值,並不要隨意針對它作優化。

不使用volatile定義的變數,可能因為編譯器優化而出現一些問題,具體可以看給出的參考資料,裡面有一個例子。

建議使用volatile變數的場所:
(1) 並行設備的硬體寄存器
(2) 一個中斷服務子程序中會訪問到的非自動變數(全局變數)
(3) 多線程應用中被幾個任務共享的變數

❾ C語言中volatile在什麼情況下使用

簡單的說,你所定義的這個變數,在你程序運行過程中一直會變,你希望這個值被正確的處理,你就得每次從內存中去讀這個值,這樣就不會有錯誤了,這個volatile就是這個作用了。

❿ 求C語言中的32個關鍵字及其意思

C語言中的32個關鍵字及其意思如下:

1、auto 聲明自動變數

2、short 聲明短整型變數或函數

3、int 聲明整型變數或函數

4、long 聲明長整型變數或函數

5、float 聲明浮點型變數或函數

6、double 聲明雙精度變數或函數

7、char 聲明字元型變數或函數

8、struct 聲明結構體變數或函數

9、union 聲明共用數據類型

10、enum 聲明枚舉類型

11、typedef 用以給數據類型取別名

12、const 聲明只讀變數

13、unsigned 聲明無符號類型變數或函數

14、signed 聲明有符號類型變數或函數

15、extern 聲明變數是在其他文件正聲明

16、register 聲明寄存器變數

17、static 聲明靜態變數

18、volatile 說明變數在程序執行中可被隱含地改變

19、void 聲明函數無返回值或無參數,聲明無類型指針

20、else 條件語句否定分支(與 if 連用)

21、switch 用於開關語句

22、case 開關語句分支

23、for 一種循環語句

24、do 循環語句的循環體

25、while 循環語句的循環條件

26、goto 無條件跳轉語句

27、continue 結束當前循環,開始下一輪循環

28、break 跳出當前循環

29、default 開關語句中的「其他」分支

30、sizeof 計算數據類型長度

31、return 子程序返回語句(可以帶參數,也可不帶參數)循環條件

32、if 條件語句

熱點內容
如何看手機已經連接了的wifi密碼 發布:2022-07-03 20:26:00 瀏覽:309
cmysql資料庫伺服器 發布:2022-07-03 20:24:22 瀏覽:997
電信卡如何激活客戶密碼 發布:2022-07-03 20:23:50 瀏覽:213
轉團之後密碼是什麼 發布:2022-07-03 20:23:45 瀏覽:488
java語言發展歷史 發布:2022-07-03 20:21:53 瀏覽:410
今日頭條文章生成腳本 發布:2022-07-03 20:20:45 瀏覽:416
安卓怎麼清理刪除的軟體 發布:2022-07-03 20:19:13 瀏覽:971
網站搭建好了伺服器也租好了 發布:2022-07-03 20:18:59 瀏覽:529
收款碼付款多少錢要輸入密碼 發布:2022-07-03 20:17:23 瀏覽:109
android遠程資料庫訪問 發布:2022-07-03 20:17:05 瀏覽:159