iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
自我挑戰組

韌體工程師的不只 0x10 個問題系列 第 19

Day 19 - volatile 的延伸問題

  • 分享至 

  • xImage
  •  

Day 18 的文章提到 volatile 的意義是告訴編譯器不要讀暫存器上的快取值,因為該變數值隨時可能被外部改變。

這包含以下三種情況:

  1. 使用硬體暫存器
  2. 中斷服務常式 ISR
  3. 多執行緒共用變數

在理解基本使用方法後,本篇文章緊接著來看延伸問題。

變數是否可同時為 const 也是 volatile

回顧 Day 6 文章中我們說:const 代表宣告後不能改變其值。

好,所以:

  • const 代表變數不能被改變值。
  • volatile 又說變數的值可能隨時被改。

所以這個變數值到底會不會被改變?

要釐清這點,我們首先得理解是「誰」在改變變數。

const 是說「程式碼」不應該改變變數的值,所以如:

const int a = 10;
a = 5;

已經賦值為 10 的變數 a 被「程式碼」改為 5 會報錯。

至於 volatile 代表的是值可能被「程式碼外部」改變,例如:

volatile int flag = 0; 

__interrupt void my_isr(void) {
    flag = 1;
}

int main() {
    while (1) {
        // ...
    }
    return 0;
}

要改變這裡的 flag 得由 __interrupt void my_isr,而 __interrupt void my_isr 是「中斷」呼叫,而不是被 main() 寫的程式碼呼叫,所以改變 flag 的是中斷,而不是我寫的程式。

總而言之:

  • const 管的是人類,告訴開發者人員「不要在程式裡面去改變這個變數」。
  • volatile 管的是編譯器,告訴編譯器「這個變數可能被程式外的東西改變」。

所以如果兩個關鍵字一起寫,例如:

const volatile int status_reg;

或者:

volatile const int status_reg;

代表 status_reg 變數不能被人類開發者改值,但是可能被程式碼以外的東西如硬體、ISR 或執行緒改值,請編譯器不要只讀快取值。

指標能否為 volatile

可以!而且還分成三種情況:

  1. 指標非 volatile、變數 volatile
  2. 指標 volatile、變數非 volatile
  3. 指標跟變數都是 volatile

指標非 volatile、變數 volatile

volatile 修飾的是變數,表示該變數的值可能隨時被外部改變、但指標沒有被外部改成指向別的地方,例如:

volatile int var = 20;

volatile int* ptr = &var;

在此範例中,var 的值隨時可能被外部改成不是 20,但 ptr 指向的記憶體位址不會頻繁被外部改變,基本上就是 &var

指標 volatile、變數非 volatile

表示變數本身的值不必每次都從記憶體讀,但指標指向的記憶體位址隨時可能被外部改變,例如:

int var = 10;

int* volatile ptr = &var;

在此範例中,var 變數存的值 10 不會頻繁被外部改變,但 ptr 不一定指向 &var,可能隨時被外部改成指向其他位址。

指標跟變數都是 volatile

表示指標指的位址、以及該位址存的變數值隨時都可能被外部改變,例如:

volatile int var = 20;

volatile int* volatile ptr = &var;

在此範例中,變數本身的值 10 可能隨時被外部改變、ptr 指的位址 &var 也可能隨時被改變,變成指向別的位址,因此不論變數或指標,都不要讀快取值。

程式碼檢視

現在我們知道 volatile 可以用在指標上,那下列程式碼會有什麼問題呢?

int square(volatile int *ptr){
    return *ptr * *ptr;
}

這裡的指標 ptr 雖然都是指向同樣的記憶體位址,但該位址存的值隨時可能被外部改變,因此兩次 *ptr 取得的值可能不同,使得 *ptr * *ptr 變成兩個相異的數相乘。

若要確保兩次 *ptr 的值相同,則應該改為:

int square(volatile int *ptr){
    int a;
    a = *ptr;    // 只讀一次 ptr 指向的值
    return a * a;
}

*ptr 的值變成只讀一次,並賦值於 a,就能確保是兩個相同的數相乘得其平方值。

參考資料

  1. 韌體工程師的0x10個問題
  2. const vs volatile in C: Why These Two Little Words Still Confuse Engineers
  3. volatile Qualifier in C++

上一篇
Day 18 - volatile 關鍵字
系列文
韌體工程師的不只 0x10 個問題19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言