簡體   English   中英

const 和 const volatile 之間的區別

[英]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 ,可能會出現一些問題:

  • while 循環測試可能只讀取狀態寄存器一次,因為編譯器可以假設它指向的任何東西都不會改變(while 循環測試或循環本身沒有任何東西可以改變它)。 如果您在 UART 硬件中沒有字符等待時進入該函數,您可能會進入一個無限循環,即使接收到一個字符也不會停止。
  • 接收寄存器的讀取可以由編譯器移動到 while 循環之前 - 再次因為函數中沒有任何內容表明*recv_reg被循環更改,沒有理由在進入循環之前無法讀取它。

volatile限定符確保編譯器不會執行這些優化。

  • volatile會告訴編譯器不要優化與變量相關的代碼,通常是當我們知道它可以從“外部”更改時,例如通過另一個線程。
  • const會告訴編譯器禁止程序修改變量的值。
  • const volatile是一個非常特別的東西,你可能會在你的生活中看到它使用了 0 次 (tm)。 正如所料,這意味着程序不能修改變量的值,但可以從外部修改該值,因此不會對變量進行優化。

並不是因為變量是常量,所以它在兩個序列點之間可能沒有改變。

Constness 是你做出的不改變價值的承諾,而不是價值不會改變。

在 C 中, constvolatile是類型限定符,這兩個是獨立的。

基本上, const意味着該值不能被程序修改。

volatile意味着該值會發生突然變化(可能來自程序外部)。

事實上,C 標准給出了一個既是const又是volatile的有效聲明的例子。 例子是:

extern const volatile int real_time_clock;

其中real_time_clock可由硬件修改,但不能分配、遞增或遞減。

所以我們應該已經分別對待constvolatile 這些類型限定符也可以應用於structunionenumtypedef

我需要在嵌入式應用程序中使用它,其中一些配置變量位於閃存區域,可由引導加載程序更新。 這些配置變量在運行時是“常量”,但如果沒有 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 更改將立即生效。

您可以同時使用constvolatile 例如,如果假定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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM