[英]Difference between const & const volatile
如果每次更新新值時我們都將變量聲明為volatile
如果我們將一個變量聲明為const
那么該變量的值將不會改變
然后const volatile int temp;
如上所述聲明變量temp
有什么用?
如果我們聲明為const int temp
會發生什么?
代碼不允許更改標記為const volatile
的對象(由於const
限定符將引發錯誤)-至少通過該特定名稱/指針。
限定符的volatile
部分意味着編譯器無法優化或重新排序對對象的訪問。
在嵌入式系統中,這通常用於訪問可由硬件讀取和更新的硬件寄存器,但寫入沒有意義(或者可能是寫入錯誤)。
一個例子可能是串行端口的狀態寄存器。 各種位將指示字符是否正在等待讀取或發送寄存器是否已准備好接受新字符(即 - 它是空的)。 根據串行端口硬件中發生的其他情況,對該狀態寄存器的每次讀取都可能導致不同的值。
寫入狀態寄存器是沒有意義的(取決於特定的硬件規格),但是您需要確保每次讀取寄存器都會導致對硬件的實際讀取 - 使用先前讀取的緩存值將不會' t 告訴您硬件狀態的變化。
一個簡單的例子:
unsigned int const volatile *status_reg; // assume these are assigned to point to the
unsigned char const volatile *recv_reg; // correct hardware addresses
#define UART_CHAR_READY 0x00000001
int get_next_char()
{
while ((*status_reg & UART_CHAR_READY) == 0) {
// do nothing but spin
}
return *recv_reg;
}
如果這些指針沒有被標記為volatile
,可能會出現一些問題:
*recv_reg
被循環更改,沒有理由在進入循環之前無法讀取它。 volatile
限定符確保編譯器不會執行這些優化。
volatile
會告訴編譯器不要優化與變量相關的代碼,通常是當我們知道它可以從“外部”更改時,例如通過另一個線程。const
會告訴編譯器禁止程序修改變量的值。const volatile
是一個非常特別的東西,你可能會在你的生活中看到它使用了 0 次 (tm)。 正如所料,這意味着程序不能修改變量的值,但可以從外部修改該值,因此不會對變量進行優化。並不是因為變量是常量,所以它在兩個序列點之間可能沒有改變。
Constness 是你做出的不改變價值的承諾,而不是價值不會改變。
在 C 中, const
和volatile
是類型限定符,這兩個是獨立的。
基本上, const
意味着該值不能被程序修改。
而volatile
意味着該值會發生突然變化(可能來自程序外部)。
事實上,C 標准給出了一個既是const
又是volatile
的有效聲明的例子。 例子是:
extern const volatile int real_time_clock;
其中real_time_clock
可由硬件修改,但不能分配、遞增或遞減。
所以我們應該已經分別對待const
和volatile
。 這些類型限定符也可以應用於struct
、 union
、 enum
和typedef
。
我需要在嵌入式應用程序中使用它,其中一些配置變量位於閃存區域,可由引導加載程序更新。 這些配置變量在運行時是“常量”,但如果沒有 volatile 限定符,編譯器會優化這樣的東西......
cantx.id = 0x10<<24 | CANID<<12 | 0;
...通過預先計算常量值並使用立即匯編指令,或從附近位置加載常量,這樣對配置閃存區域中原始 CANID 值的任何更新都將被忽略。 CANID 必須是 const 可變的。
const
表示變量不能被 c 代碼修改,而不是它不能改變。 這意味着沒有指令可以寫入變量,但它的值可能仍然會改變。
volatile
意味着該變量可能隨時更改,因此可能不會使用緩存值; 對變量的每次訪問都必須執行到其內存地址。
由於問題被標記為“嵌入式”並且假設temp
是用戶聲明的變量,而不是與硬件相關的寄存器(因為這些通常在單獨的 .h 文件中處理),請考慮:
具有易失性讀寫數據存儲器 (RAM) 和非易失性只讀數據存儲器的嵌入式處理器,例如馮諾依曼架構中的閃存,其中數據和程序空間共享公共數據和地址總線。
如果你聲明const temp
有一個值(至少在不等於 0 的情況下),編譯器會將該變量分配給 FLASH 空間中的一個地址,因為即使它被分配給一個 RAM 地址,它仍然需要 FLASH 存儲器來存儲變量的初始值,使 RAM 地址浪費空間,因為所有操作都是只讀的。
結果:
int temp;
是存儲在 RAM 中的變量,在啟動 (cstart) 時初始化為 0,可以使用緩存值。
const int temp;
是存儲在(只讀)FLASH 中的變量,在編譯器時初始化為 0,可以使用緩存值。
volatile int temp;
是存儲在 RAM 中的變量,在啟動 (cstart) 時初始化為 0,不會使用緩存值。
const volatile int temp;
是存儲在(只讀)FLASH 中的變量,在編譯器時初始化為 0,不會使用緩存值
有用的部分來了:
如今,大多數嵌入式處理器都能夠通過特殊功能模塊對其只讀非易失性存儲器進行更改,在這種情況const int temp
可以在運行時更改const int temp
,而不是直接更改。 換句話說,函數可以修改存儲temp
的地址處的值。
一個實際的例子是使用temp
作為設備序列號。 嵌入式處理器第一次運行時, temp
將等於 0(或聲明的值),一個函數可以使用這個事實在生產過程中運行測試,如果成功,要求分配一個序列號並修改temp
的值通過特殊功能。 一些處理器有一個特殊的地址范圍,帶有 OTP(一次性可編程)內存。
但不同之處在於:
如果const int temp
是一個可修改的 ID 而不是一次性可編程的序列號,並且未聲明為volatile
,則可能會使用緩存值直到下一次啟動,這意味着新 ID 可能在下一次重新啟動之前無效,甚至更糟糕的是,某些功能可能會使用新值,而其他功能可能會使用較舊的緩存值,直到重新啟動。 如果const int temp
IS 聲明為voltaile
,則 ID 更改將立即生效。
您可以同時使用const
和volatile
。 例如,如果假定0x30
是僅由外部條件更改的端口值,則以下聲明將防止任何意外副作用的可能性:
const volatile char *port = (const volatile char *)0x30;
本文討論了您想要組合 const 和 volatile 限定符的場景。
http://embeddedgurus.com/barr-code/2012/01/combining-cs-volatile-and-const-keywords/
簡單來說,'const volatile' 變量中的值不能以編程方式修改,但可以通過硬件修改。 Volatile 是為了防止任何編譯器優化。
當我們不想讓程序改變它時,我們對變量使用“const”關鍵字。 而當我們聲明一個變量“const volatile”時,我們是在告訴程序不要改變它,編譯器可以從外部世界的輸入中意外地改變這個變量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.