99久久全国免费观看_国产一区二区三区四区五区VM_久久www人成免费看片中文_国产高清在线a视频大全_深夜福利www_日韩一级成人av

徐土豆
認證:優質創作者
所在專題目錄 查看專題
C語言中內循環和外循環的位置可能產生性能上的區別
[C語言朝花夕拾] C語言中的命令行輸入參數判斷
用“位操作”取代“取模操作”判斷奇數偶數
c語言運行時出現segment fault的原因
一文理解C語言中的volatile修飾符
C語言中的內存布局(memory layout)
作者動態 更多
給定計算預算下的最佳LLM模型尺寸與預訓練數據量分配
05-19 09:33
大模型推理時的尺度擴展定律
05-18 10:32
世界多胞體與世界模型
05-13 09:42
獎勵模型中的尺度擴展定律和獎勵劫持
05-12 08:41
MeCo——給預訓練數據增加源信息,就能減少33%的訓練量并且提升效果
05-08 09:13

一文理解C語言中的volatile修飾符

本文轉自徐飛翔的“一文理解C語言中的volatile修飾符

版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

前言

volatile修飾符是在嵌入式開發和多線程并發編程中常見的修飾符,理解其對于實踐過程非常有幫助,此文參考了[1],并且附上了筆者的一些例子,希望對大家有所幫助。

volatile修飾符用于C語言和C++中,其意在阻止編譯器對其修飾對象進行任何形式的優化,有時候,這種編譯器“自作主張”的優化會導致編程者意想不到的結果,因此需要引入這個關鍵字進行限制。

當一個對象可能會被當前代碼以外的環境,在任何時刻被改變的時候,一個對象如果此時被聲明為了volatile,那么其就可以脫離編譯器的優化過程。當需要讀取該數據的時候,系統總是會重新從內存位置中讀取當前volatile類型的數據,而不是直接取其在寄存器中的值,我們要知道,為了執行效率,即便是你指定了要從之前的同一個對象中取值,編譯器在優化過程中也很可能會不直接從內存中讀取數據,而是直接采用寄存器中的值(我們將會從后續的例子中看到這個情況。),這個行為在一般情況下的確能夠提高程序的執行速率,畢竟數據從寄存器中讀取,要比從內存中讀取快上好幾個數量級。比如我們見一個簡單例子:

// sample.c
int main(){
    int i = 10;
    i = i;
    return 0;
}

在linux下用命令gcc -S sample.c編譯,我們得到了其匯編結果,我截取了其主體,如:

movl  $10, -4(%rbp)

而在sample.c的變量聲明中如果加上volatile修飾符,那么程序變成:

// sample.c
int main(){
    volatile int i = 10;
    i = i;
    return 0;
}

匯編后的結果為:

movl  $10, -4(%rbp)
movl  -4(%rbp), %eax
movl  %eax, -4(%rbp)

我們對比這兩次的匯編結果,我們發現,第一次沒有聲明其為易變的時候,編譯器分析了代碼的變量的關系,并且進行了優化,編譯器認為,我的變量i既然在下一步還需要賦值給自己,那么何必在重新從內存中讀取i的值呢,因此,從匯編來看,i = i這條c語言代碼其實是無效的。 在一般的編譯器優化中,因為編譯器可能會認為變量i是非易變的,如果其變化了,只能是因為程序員對其進行了顯式的賦值改變,因此在需要再次讀取變量i的值的時候,與其重新從內存中讀取,不如直接利用其已經讀入到寄存器中的值,畢竟寄存器比內存快得多。

但是我們要思考下,i = i;是不是沒有意義的代碼呢?我們很容易認為這個答案 是的確沒有意義。

但是,我們假設有一種情況,在int i = 10;之后,因為某種原因,比如硬件中斷,多線程的修改或者其他原因,導致此時i改變了,而不是初始的i = 10了,那么我們后續的代碼i = i;就變得非常重要,因為其需要讀取在內存中,新的值i,而不是簡單的將其忽視掉或者簡單地讀取內存中的值,注意到這個時候寄存器中的值已經是“過時”了的,如果任由編譯器去優化,那么你將永遠無法讀取傳感器的值(傳感器的值很多由硬件中斷讀取。)

通過上面的討論,我們便能理解這兩個不同的匯編結果了,在第二段匯編中,我們不僅通過movl $10, -4(%rbp)將直接數10傳輸到了內存-4(%rbp) (指的是寄存器%rbp中的地址所指向的內存偏移4個字節的內存位置,是相對尋址的指令),而且接下來還重新讀取了該內存位置的值,并且將其賦給了自己的這個內存位置(這個過程中,因為該變量可能是易變的,因此該內存可能會被其他程序給覆蓋,因此要重新讀取)。

重新回到我們的討論,那么什么時候我們需要用volatile這個修飾符呢?當屬于下面幾種情況的時候,應該考慮這個修飾符:

  1. 當全局變量會被中斷服務函數給修改的時候。例如一個全局變量可以表示一個外部數據接口(通常全局指針被引用為內存映射IO),這意味著該數據會被動態地更新。如果我們的代碼期望讀取數據接口的值,那么我們就應該將其定義為volatile,以獲取其數據的最新值。如果我們不這么做,編譯器的優化過程會使得只讀取一次該接口的數據,并且將其加載到寄存器中,接下來都只能讀取該寄存器中的舊值了。
  2. 在多線程應用中的全局變量。在多線程通信中,有著多種通信方式:信號傳遞(message passing),郵箱(mail boxes),共享內存(shared memory)等。一個全局變量是共享內存的樸素形式。當兩個線程通過全局變量共享信息時,他們需要用volatile進行修飾。因為線程是異步運行的,每個線程導致的全局變量的每次更新,都應該被其他線程重新從內存中獲取。為了消除編譯器優化導致的效果,這些全局變量必須要用volatile修飾。

如果我們不用volatile修飾,有可能會導致以下問題:

  1. 當編譯器優化開啟時,代碼可能不會正常工作。
  2. 當中斷發生時,代碼可能不會正常工作。

Reference

[1]. https://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c/

聲明:本內容為作者獨立觀點,不代表電子星球立場。未經允許不得轉載。授權事宜與稿件投訴,請聯系:editor@netbroad.com
覺得內容不錯的朋友,別忘了一鍵三連哦!
贊 4
收藏 6
關注 52
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧
主站蜘蛛池模板: 日韩欧美在线播放 | 欧美性猛交╳XXX富婆 | 国产首页天堂在线 | 久久亚洲精品无码gv | 一级做a爰片性色毛片99高清 | 夜夜爽WWW | 欧美精品久久久久久久久免 | 日本国产免费 | 亚洲精品亚洲人成在线观看麻豆 | 在线观看va | 蜜桃视频在线观看一区 | 亚洲国产精品一区二区尤物区 | xxxx交换夫妇com1 | 日韩欧精品无码三级片 | 久久久久高清毛片一级 | 国产亚洲欧美日韩高清 | 久久精品中文字幕一区二区三区 | 久久有精品 | 成人麻豆日韩在无码视频 | 国产精品久久久久久久婷婷 | 91精选在线观看 | 超碰中文字幕在线 | 日韩中文字幕无码高清毛片 | 色综合精品视频 | 国产人妻人伦精品1国产盗摄 | 国产午夜精品久久久久久免费视 | 日韩一区二区精品葵司在线 | 精品久久久久久久久久中文字幕 | 偷窥xxxx盗摄国产 | 无码精品人妻免费 | 麻豆人妻少妇精品无码专区 | 91污在线观看 | 欲求不满的寂寞人妻中文字幕 | 911久久香蕉国产线看观看 | wwwwxxxxx日本| 日本黄色视 | 四虎最新影院 | 国外欧美一区另类中文字幕 | 蜜桃久久精品乱码一区二区 | 韩国午夜影院 | 成人综合激情 |