【软件开发底层知识修炼】二十八 C/C++中volatile的作用
- 上一篇文章學(xué)習(xí)了C/C++中的指針與數(shù)組的區(qū)別,點(diǎn)擊鏈接進(jìn)行查看:【軟件開發(fā)底層知識(shí)修煉】二十七 C/C++中的指針與數(shù)組是不同的
- 本篇文章將學(xué)習(xí)volatile關(guān)鍵字在C/C++中的作用
文章目錄
- 1 實(shí)例代碼分析
- 2 問題分析
- 3 解決方案
- 4 拓展: const和volatile
- 4 總結(jié)
1 實(shí)例代碼分析
在講解volatile關(guān)鍵字的作用之前,我們先來看一個(gè)代碼的例子,代碼如下:
- main.c
- device.c
上面的代碼,是非常容易懂的,這里就不再多說。我們直接編譯運(yùn)行看看:
- gcc -pthread main.c device.c -o test.out
- ./test.out
運(yùn)行結(jié)果如下:
這個(gè)結(jié)果對(duì)于我們來說其實(shí)挺容易懂的。就是main中的while循環(huán)先每隔一秒打印執(zhí)行一次,然后5秒后th_fn函數(shù)開始執(zhí)行,th_fn函數(shù)將g_ready變量變?yōu)?,然后打印一個(gè)語句。最后回到main中的while循環(huán),由于while中上次最后一秒現(xiàn)在才執(zhí)行完,打印一句,然后由于此時(shí)g_ready為1,所以退出循環(huán),然后打印循環(huán)外的一個(gè)語句,然后結(jié)束程序的運(yùn)行。這種結(jié)果,其實(shí)是比較容易理解的。也是比較普遍的結(jié)果。
但是如果我們?cè)倬幾g上述代碼的時(shí)候,加上了-O3選項(xiàng),該選項(xiàng)是優(yōu)化選項(xiàng),且級(jí)別比較高。編譯運(yùn)行結(jié)果如下:
- gcc -O3 -pthread main.c device.c -o test.out
- ./test.out
運(yùn)行結(jié)果如下動(dòng)態(tài)圖:
由以上結(jié)果我們可以看到,程序陷入了死循環(huán),g_ready沒有被改變一直都是0,所以main中的while循環(huán)一直在執(zhí)行。對(duì)于這個(gè)結(jié)果,可能并不是所有人都知道為什么。下面,我們就要來講解為什么會(huì)產(chǎn)生上面的運(yùn)行結(jié)果。
2 問題分析
-O3選項(xiàng)是讓編譯器對(duì)代碼進(jìn)行優(yōu)化。
- 編譯優(yōu)化時(shí),編譯器根據(jù)當(dāng)前文件進(jìn)行優(yōu)化。
- 為了效率上的提高,優(yōu)化時(shí)編譯器將變量值從內(nèi)存中讀取進(jìn)入寄存器
- 每次要訪問變量時(shí)直接從寄存器讀取。畢竟寄存器的存取比內(nèi)存的存取快很多。
那么,我們明白了優(yōu)化對(duì)變量的影響。對(duì)于上述的代碼,如果在編譯時(shí)加了-O3選項(xiàng)。編譯優(yōu)化時(shí),編譯器會(huì)將變量g_ready的值放到寄存器中,以后每次使用該變量就從寄存器中取出g_ready的值使用即可。這樣雖然是速度快了,但是當(dāng)在th_fn函數(shù)中改變了g_ready的值后,在內(nèi)存中確實(shí)g_ready的值已經(jīng)變?yōu)?了,但是在剛剛那個(gè)寄存器中,最開始將g_ready的值也就是0存進(jìn)去的,一直都沒有改變過,但是呢,由于編譯器的優(yōu)化,每次在使用g_ready的變量的時(shí)候,都是從寄存器中直接取出值來使用…
說到這里,應(yīng)該都能夠明白了,此時(shí)從寄存器中取出的值就一直都是0.然后while一直循環(huán)。
3 解決方案
編譯的時(shí)候使用-O3選項(xiàng)是很常用的。那么如何才能既使用這個(gè)-O3選項(xiàng),又使得上述程序按照我們的意愿來執(zhí)行呢?volatile就此出場(chǎng)。
使用volatile關(guān)鍵字修飾可能被意外修改的變量(內(nèi)存),從而禁止編譯器對(duì)該變量進(jìn)行優(yōu)化。
- volatile一般修飾的是一種易變的變量
- volatile可理解為一種編譯器警告指示字,它告訴編譯器必須每次都直接去內(nèi)存中取變量值。
那么在上述的代碼中,我們將main.c中的extern const int g_ready; 改為:extern volatile const int g_ready; 再重新進(jìn)行優(yōu)化的編譯,然后運(yùn)行,結(jié)果就是正確的運(yùn)行。可以自己嘗試做實(shí)驗(yàn)!
4 拓展: const和volatile
細(xì)心的朋友會(huì)發(fā)現(xiàn),在main.c程序中,我們改完后,成這樣了:extern volatile const int g_ready; 又是const又是volatile的。我們上面說過,volatile修飾的是意變的變量,而const修飾的變量是不能被修改的,有的人叫它常量,不可變的。而實(shí)際上在編程語言中的解釋是這樣的:const修飾的變量在當(dāng)前文件中不能夠出現(xiàn)在賦值符號(hào)的左邊。 所以我們可以看到,在main.c這個(gè)文件中g(shù)_ready這個(gè)變量,并沒有出現(xiàn)在賦值符號(hào)的左邊,所以是沒有問題。但是在device.c文件中g(shù)_ready是沒有被const修飾的,它就可以出現(xiàn)在賦值符號(hào)的左邊,可以被改變。這樣一來,在device.c中修改g_ready這個(gè)變量,在main.c中的g_ready也間接被改變了。所以,我覺得說const修飾的是常量這個(gè)說法不夠準(zhǔn)確,說它是變量但是不能夠出現(xiàn)在賦值符號(hào)的左邊更加準(zhǔn)確。
下面就總結(jié)一下const和volatile關(guān)鍵字:
- const表示修飾的變量在當(dāng)前文件中不能出現(xiàn)在賦值符號(hào)的左邊,不能直接被改變,但是可以間接被改變
- volatile表示修飾的變量每次使用的時(shí)候直接從內(nèi)存中讀取
- const和volatile同時(shí)修飾變量時(shí)互不影響。
4 總結(jié)
- 編譯優(yōu)化時(shí),編譯器僅根據(jù)當(dāng)前文件進(jìn)行優(yōu)化
- 編譯器的優(yōu)化策略可能造成一些意外
- volatile強(qiáng)制編譯器必須從內(nèi)存中取變量值
- const和volatile同時(shí)修改變量時(shí),互相不影響彼此的含義。
總結(jié)
以上是生活随笔為你收集整理的【软件开发底层知识修炼】二十八 C/C++中volatile的作用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 娃哈哈的新品,为什么打动不了年轻人?
- 下一篇: C++ 中explicit的使用