自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

噓!關(guān)于 volatile 的小秘密,知道的人不到 1%,但能幫你避開 99% 的坑 !

開發(fā)
今天咱們聊一個在 C 語言面試中經(jīng)常被問到,但平時寫代碼卻很少用到的關(guān)鍵字——volatile。

大家好,我是小康。

一、前言:看見它就頭疼?不存在的!

嘿,朋友,今天咱們聊一個在 C 語言面試中經(jīng)常被問到,但平時寫代碼卻很少用到的關(guān)鍵字——volatile。每次看到這個單詞,你是不是也和我一樣直接跳過去了?或者勉強記住它是"易變的",然后就完事了?

那好,現(xiàn)在咱們就來徹底搞懂這個被嚴重誤解的 C 語言特性,看完之后你會恍然大悟:"原來如此簡單!"

二、什么是volatile?先別急著背概念!

與其死記硬背概念,不如先從一個真實的故事開始:

小王是個程序員,他寫了段代碼來控制一個 LED 燈:

int main() {
    // LED_STATUS是個內(nèi)存映射寄存器,代表LED的狀態(tài)
    int* LED_STATUS = (int*)0x40000000;  
    
    // 點亮LED
    *LED_STATUS = 1;
    
    // 等待一段時間
    for(int i = 0; i < 1000000; i++) {
        // 空循環(huán),只是為了等待
    }
    
    // 熄滅LED
    *LED_STATUS = 0;
    
    return0;
}

小王信心滿滿地編譯代碼,燒錄到單片機里,結(jié)果 LED 燈壓根就沒亮過!

為啥?因為編譯器太聰明了!

三、被"優(yōu)化"掉的代碼

現(xiàn)代編譯器非常聰明,它看到你這段代碼會想:

  • 嗯,*LED_STATUS = 1,把這個地址的值設(shè)為 1
  • 然后空循環(huán)一段時間,啥也沒干
  • 再把*LED_STATUS = 0,把這個地址的值設(shè)為 0
  • 整個過程中,程序沒讀取過*LED_STATUS的值

所以編譯器一拍腦門:"這不是多此一舉嗎?先設(shè) 1 再設(shè) 0,我直接優(yōu)化成只設(shè) 0 得了!而且這個空循環(huán)啥也沒干,也可以優(yōu)化掉!"

于是最終編譯出來的代碼變成了:

int main() {
    int* LED_STATUS = (int*)0x40000000;  
    *LED_STATUS = 0;  // 只保留了最后一次賦值
    return 0;
}

LED 當(dāng)然不會亮啦!

四、volatile來救場!

這時候就需要我們的主角volatile出場了:

int main() {
    // 加了volatile關(guān)鍵字
    volatile int* LED_STATUS = (int*)0x40000000;  
    
    *LED_STATUS = 1;  // 這行不會被優(yōu)化掉
    
    for(int i = 0; i < 1000000; i++) {
        // 空循環(huán)也不會被完全優(yōu)化掉
    }
    
    *LED_STATUS = 0;
    
    return 0;
}

就這么簡單一改,LED 就能正常工作了!為啥呢?

五、volatile的真正含義:別自作聰明,編譯器!

volatile 關(guān)鍵字就是告訴編譯器:"這個變量可能會被意想不到的方式修改,所以每次使用它時都要老老實實地從內(nèi)存讀取,每次改它時都要老老實實地寫入內(nèi)存,千萬別耍小聰明優(yōu)化掉我的操作!"

具體來說,volatile 主要有兩個作用:

  • 禁止優(yōu)化:編譯器不會優(yōu)化掉對 volatile 變量的讀寫操作
  • 防止重排序:保證程序按照你寫的順序來訪問 volatile 變量

需要注意的是,在 C 語言中,volatile 并不保證"內(nèi)存可見性"!這是個常見誤解。在 C 語言標準中,volatile 只是告訴編譯器不要優(yōu)化,但不保證不同 CPU 核心或線程之間的內(nèi)存可見性,這一點與 Java 等語言的 volatile 不同。

六、什么時候我們需要用volatile?

volatile 主要用在以下三種場景:

1. 硬件寄存器(最常見)

像剛才 LED 的例子,訪問的是硬件寄存器。寄存器的值可能會被硬件自己改變,編譯器不知道,所以需要 volatile 告訴它。

例如:

volatile unsigned int* timer_register = (unsigned int*)0x40001000;

2. 多線程共享變量(但要小心!)

很多人以為在多線程程序中,volatile 可以保證一個線程修改變量后,另一個線程能立即看到最新值。但在 C 語言中,這其實是不保證的!

volatile int shared_flag = 0;

// 線程1
void thread1() {
    shared_flag = 1;  // 修改共享變量
}

// 線程2
void thread2() {
    while(!shared_flag) {
        // 等待shared_flag變?yōu)?
        // 但在某些架構(gòu)上,可能會一直卡在這!
    }
    // 繼續(xù)執(zhí)行...
}

在現(xiàn)代多核 CPU 上,每個核心都有自己的緩存。volatile 只保證編譯器不做優(yōu)化,但不保證 CPU 緩存的一致性!所以在多線程編程中,應(yīng)該使用原子操作、互斥量或內(nèi)存屏障,而不是僅僅依賴 volatile。

3. 信號處理函數(shù)中的變量

在信號處理函數(shù)中修改的變量,主程序也要能看到,這時也需要 volatile。

volatile int signal_occurred = 0;

void signal_handler(int sig) {
    signal_occurred = 1;
}

int main() {
    signal(SIGINT, signal_handler);
    
    while(!signal_occurred) {
        // 主循環(huán)
    }
    
    // 處理信號...
    return 0;
}

七、volatile的常見誤解

很多人對 volatile 有誤解,例如:

(1) 誤解:volatile 可以保證內(nèi)存可見性

糾正:在 C 語言中,volatile 不保證不同 CPU 核心之間的緩存一致性和內(nèi)存可見性

(2) 誤解:volatile 可以保證原子性

糾正:volatile 只告訴編譯器不要優(yōu)化,不保證操作的原子性

(3) 誤解:volatile 可以替代鎖

糾正:不行,鎖不僅提供原子性和內(nèi)存可見性,還提供互斥訪問(同一時間只允許一個線程訪問共享資源),這是 volatile 根本無法做到的

(4) 誤解:所有全局變量都應(yīng)該用 volatile

糾正:只有在上面提到的特殊場景下才需要用 volatile

(5) 誤解:C 和 Java 中的 volatile 作用相同

糾正:完全不同!Java 的 volatile 確實能保證內(nèi)存可見性,而 C 語言中不保證

八、一個生動的比喻

把 volatile 理解成"易變的"不太直觀,我們不如把它想象成"警告標簽":

想象你家里有個裝滿熱水的水壺,你貼了個"小心燙"的標簽(volatile)。這個標簽告訴所有人(編譯器):

  • 別直接用手摸(別做優(yōu)化)
  • 真的需要用水時要小心(每次都從內(nèi)存訪問,不用寄存器緩存)
  • 別隨便移動它的位置(別重排序)

但要注意,這個標簽只對直接看到它的人有用。如果你家里有兩個人(兩個CPU核心),一個人看到標簽知道水很燙,但另一個人可能根本沒注意到這個標簽!這就是為什么 volatile 不保證多核 CPU 之間的內(nèi)存可見性。

九、總結(jié):記住這五點就夠了

  • volatile 告訴編譯器:"這個變量隨時可能變,別優(yōu)化我對它的操作"
  • 在 C 語言中,volatile 不保證多線程/多核心之間的內(nèi)存可見性(與 Java 不同)
  • 主要用于:硬件寄存器、信號處理,以及特定條件下的多線程共享變量
  • volatile 既不保證原子性,也不保證內(nèi)存可見性,不能替代鎖或內(nèi)存屏障
  • 不要濫用 volatile,會影響性能

好了,現(xiàn)在你應(yīng)該對 volatile 這個"被嚴重誤解的C語言特性"有了清晰的理解。下次面試官再問你,你就能輕松應(yīng)對啦!

責(zé)任編輯:趙寧寧 來源: 跟著小康學(xué)編程
相關(guān)推薦

2019-08-30 08:39:33

WebSocketNginx服務(wù)器

2019-07-22 09:46:28

WebSocketNginx服務(wù)器

2013-11-25 10:43:32

谷歌微軟

2018-08-15 08:47:20

2019-09-10 16:25:19

Python內(nèi)存空對象

2016-01-08 14:23:55

2012-03-23 10:27:08

觸屏手機點擊區(qū)域

2025-03-19 08:40:00

2017-12-15 21:46:45

2010-05-13 00:03:44

2015-03-09 09:34:04

C語言函數(shù)指針

2011-12-09 17:41:56

2010-10-12 12:10:52

增強無線網(wǎng)絡(luò)信號

2023-12-12 08:50:22

MySQL隱式轉(zhuǎn)換varchar

2015-04-14 09:46:09

Apple Watch秘密

2016-03-31 14:51:33

多云計算多云部署多云管理

2020-01-15 12:32:02

大數(shù)據(jù)人工智能技術(shù)

2015-11-27 10:13:19

數(shù)據(jù)中心

2015-03-06 10:33:02

2019-06-05 12:49:07

云辦公
點贊
收藏

51CTO技術(shù)棧公眾號