Day 18 的文章提到 volatile 的意義是告訴編譯器不要讀暫存器上的快取值,因為該變數值隨時可能被外部改變。
這包含以下三種情況:
在理解基本使用方法後,本篇文章緊接著來看延伸問題。
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?可以!而且還分成三種情況:
volatile、變數 volatile。volatile、變數非 volatile。volatile。volatile、變數 volatilevolatile 修飾的是變數,表示該變數的值可能隨時被外部改變、但指標沒有被外部改成指向別的地方,例如:
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,就能確保是兩個相同的數相乘得其平方值。