C語言之不變量(const)

2021-01-07 魚鷹談單片機

我們知道,數據分為兩種,一種為只讀,一種為可讀可寫,為了防止一些不變的數據被程序意外的修改,有必要對它進行保護。這就是 const 的作用。在單片機中,不變的數據(比如代碼,比如用戶一些固定不變的數據)一般存放在 FLASH 中,而 FLASH 在一般情況下是只讀的(事實上可以通過操作修改 FLASH,這種方式稱之為 IAP ,即你可以不斷更新程序的原因),而一些可變的數據一般是放在 RAM 中的,這些數據可能被指針意外的修改。所以就需要通過一種方式來將這些數據存放在 FLASH 中以防意外修改,而最好的保護就是硬體水平上的支持,這就是 const 的作用,它可以將你的數據放在 FLASH 中,但它的作用不只是如此。

const: 限定一個變量不允許改變,產生靜態作用,const 在一定程度上可以提高程序的安全性和可靠性。

const 推出的初始目的,正是為了取代預編譯指令,消除它的缺點,同時繼承它的優點。

聲明為 const 的變量是不能被用戶改變的(意思就是說你不允許你通過代碼去修改這個值),因為編譯器會將該變量放在只讀區,比如在 KEIL 開發平臺下,聲明為 const 的變量放在 FLASH 區,這樣即使你使用取地址符 & 獲取聲明為 const 變量地址,並通過指針進行修改,雖然編譯器不報錯,但也是無法進行修改的,因為 STM32 進行 FLASH 編程是有條件的。

你會發現雖然 p 獲取了 N 的地址,但因為 N 存放在 FLAH(0x0800 0000 開始地址是 FLASH)中,所以即使通過指針的方式也是無法間接改變N的值的。

可以看到運行 *p = 4 的代碼後,N 並沒有發生改變。但是編譯器確實也沒報錯。但是如果你直接使用 N = 4; 的話,肯定是報錯的,因為你的N已經被申明為 const 了。

《C語言深度剖析》中關於 const 的介紹發現和 KEIL 情況不一樣。

在 KEIL 中進行相關代碼的編寫,編譯,最後可以看到如下結果:

這是仿真模式下兩個地址的內容,一個存放在 FLASH,一個存放在 RAM 中,並且當修改 FLAH 的內容之後(因為是軟體仿真模式,可以直接修改值),復位重新運行,你會發現 RAM 的內容對應改變了(重新運行後,進入 main 函數之前,有一段拷貝代碼,就是函數外申明的一些變量的初始化過程),這就說明,在 STM32、KEIL 環境下,並不是《C語言深度剖析》中說的只有一份內存,而是每一個都有一個,申明為 const 情況跟使用宏定義的方式是一樣的。

以下是 Watch 中的內容:

但其實上面的結論是在使用 & 將 N 的地址獲取後的結果(從上圖可以看到 p 的值),實際上代碼中如果沒有獲取 N 的地址時,情況又不一樣了。

內存情況:

在刪去獲取 N 地址後的內存情況,可以發現 N 的值為 0x2000470,和 FLAH 地址 0x08000000 一樣。

這像一個地址。但通過 Memory 查看這個地址發現存放的不是 5。

根據 ARM 內核的知識可以知道,0x08000000 地址存放的其實是棧頂指針,也就是說 N 存放的是棧頂指針嗎?顯然不是。

然後對 .map 地址映射文件進行搜索,你會發現,根本沒有 N 的地址。這樣說來,N 在內存的位置對用戶是不可見的,而是由編譯器自動處理了。

那麼有沒有辦法找到這個拷貝源頭呢。之前我說過,先前能找到拷貝的源頭純屬偶然,有沒有什麼方法可以找到呢?這其中的難點就是進入 main 函數之前的那段拷貝代碼不是我們用戶自己寫的,而是 C 編譯器自動處理的,怎麼辦?

這個時候就需要請出一個關鍵人物:數據觀察點(關於數據觀察點,將有專門的一小節詳細說明,感興趣的可以關注我)。

我們知道,不管如何,因為 FLASH 存放著變量初始值,然後在程序運行的時候才將 FLASH中的值初始化到 RAM 中去,也就是我們使用的 RAM 變量,那麼必然存在通過總線進行數據傳輸的過程,所以可以通過數據觀察點的功能實現對地址的監控,雖然我們不知道 FLASH 的地址,但是我們知道 RAM 的地址,所以只要對變量i進行監控,就可以通過內核的寄存器找到 FLASH 地址了。如下:

這裡的 0x20000000 就是 i 的 RAM 地址,最終可以找到 FLASH 的位置:

由此可以知道,FLAH 中也是有多個相同副本存在的。

因此可以得出結論,在 STM32、KEIL 的環境下,《C語言深度剖析》對於兩者的說法在這裡不適用。

看了那麼多,沒有足夠的基礎是很難知道我在講什麼,下面用一張圖進行說明,希望可以解答你的疑惑。

希望你在看完這張圖之後,再回過頭看看前面的那些話,對你應該會有幫助的。

喜歡本系列 C 語言文章的可以關注我哦!

相關焦點

  • 強制了解const,C語言變量和常量!C語言系列教程!編號零零六
    C語言變量AC程式語言中的變量是指定的存儲器位置,用戶可以在程序執行期間存儲相同數據類型的不同值。這意味著變量是給予內存位置的名稱,我們可以在其中存儲相同數據類型的不同值。換句話說,變量可以定義為在程序執行期間保存相同數據類型值的存儲容器。數據類型的正式定義如下......
  • C語言之const和volatile"究極"學習
    2、const全局變量的分歧:在現代c語言編譯器中,修改const全局變量將導致程序崩潰標準c語言編譯器不會將const修飾的全局變量存儲於只讀存儲區中,而是存儲於可修改的全局數據區,其值依然可以改變3、代碼示例:(1)只讀變量代碼示例:#include <stdio.h>int main(){
  • C語言const 關鍵字
    const和變量const uint32_t hello = 3;編譯的時候,編譯器就知道了 hello 這個變量是不可以被修改了,const其實也就是read only,你只能讀我的,不能修改我。所以對分身的屬性和本體的屬性是不一樣的。
  • C/C+編程筆記:const 變量詳解!一文了解具體用法
    由於分文件編寫,不好呈現,所以在這裡不為大家提供c語言全局const變量默認為外部聯編的案例。如果各讀者有興趣,可自行嘗試。 3、C/C++中const異同總結 c語言全局const會被存儲到只讀數據段。
  • 嵌入式C語言中 const 的多種用法
    創建常量的格式通常為:const 和 type 都是用來修飾變量的,它們的位置可以互換,也就是將 type 放在 const 前面:但我們通常採用第一種方式,不採用第二種方式。另外建議將常量名的首字母大寫,以提醒程式設計師這是個常量。由於常量一旦被創建後其值就不能再改變,所以常量必須在定義的同時賦值(初始化),後面的任何賦值行為都將引發錯誤。
  • C語言中auto register static const volatile 區別
    1)auto這個關鍵字用於聲明變量的生存期為自動,即將不在任何類、結構、枚舉、聯合和函數中定義的變量視為全局變量,而在函數中定義的變量視為局部變量。這個關鍵字不怎麼多寫,因為所有的變量默認就是auto的。
  • C語言全局變量那些事兒
    事實上,這種規則是C語言裡的一個大坑,編譯器對這種全局變量多重定義的「縱容」很可能會無端修改某個變量,導致程序不確定行為。如果你還沒有意識到事態嚴重性,我再舉個例子。不過這次編譯器倒是給出了變量b的sizeof決議警告。到此為止,有些人可能會對上面的例子嗤之以鼻,覺得這不過是列舉了C語言的某些特性而已,算不上黑。有些人認為既然如此,對於一切全局變量要麼用static限死,要麼定義同時初始化,杜絕弱符號,以便在編譯時報錯檢測出來。
  • C語言中const關鍵字的妙用總結
    C語言中const關鍵字是constant的縮寫,通常翻譯為常量、常數等,有些朋友一看到const關鍵字馬上就想到了常量。事實上在C語言中const功能很強大,它可以修飾變量、數組、指針、函數參數等。本文將針對上述幾種修飾功能詳細做一個總結。
  • C語言丨const關鍵字的用法詳解
    來源:小職(z_zhizuobiao)找我:✅ 解鎖高薪工作 ✅ 免費獲取乾貨教程這篇文章主要對C語言中const關鍵字的用法進行了詳細的分析介紹,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小夥伴們可以參考一下。const 在實際編程中用得並不多,const 是 constant 的縮寫,意思是「恆定不變的」!
  • 【專業技術第二講】c語言中const的使用
    這裡對const的使用做一個大致的總結。C語言的const關鍵字與指針搭配使用,const是C語言中保留的一個關鍵字,它用來限定一個變量是只讀的,即不可變的。(1)用const修飾一般變量注意在C語言中,用const修飾的變量必須在聲明時進行初始化(用來修飾函數的形參除外);  如:  const int n; 這種聲明方式是錯誤的  const int n=5; 正確  void fun(const
  • const 並不能加快 C 代碼的運行速度?
    doubleIt((int*)x);}voiddoubleIt(int *x){ *x *= 2;}localVar()給了constFunc() 一個指向非const變量的const指針。因為變量原本不是const,所以constFunc()可能會撒謊,在不觸發「未定義的行為」的情況下強制修改它。因此編譯器不能假設在constFunc()返回後,這個變量的值仍然不變。
  • C++與C中的const關鍵字有何差別?
    前言在《const關鍵字到底該怎麼用》一文中介紹了C語言中的const關鍵字,本文說說C++中的const關鍵字,它的大部分特點和C語言中的類似,所以本文主要針對不同之處
  • C 語言學習之變量、數據類型
    語言是強數據類型語言,即聲明變量時,需要定義好變量的類型。變量相當於內存中一個數據存儲空間的表示,通過變量名可以訪問到該變量【值】,變量使用的基本步驟:int main(){ int a = 0;
  • 再論C++中的const和引用
    (1)const常量的判別規則:只用字面量初始化的const常量才會進入符號表使用其它變量初始化的const常量仍然是只讀變量被volatile修飾的const常量不會進入符號表在編譯期間不能直接確定初始值的const標識符,都被作為只讀變量處理(2)const引用的類型與初始化變量的類型相同:初始化變量成為只讀變量
  • C語言之 static
    頭文件中使用前面三個應該是很常見的,簡要說明即可,重要的是後面那一個不常見,作為重點講解內容。這個和局部變量的性質是一樣的,但是這裡又加了一個屬性:靜態,這就意味著這個變量可以始終存在,不會說函數調用完了,我就不存在了。這兩種變量能始終存在就是因為編譯器將這兩種變量和全局變量放在一個內存區域了。靜態函數:函數體是始終存在的,但是這裡加了一個 static 什麼意思,肯定不是讓它始終存在,而是限制它的作用域。
  • 第2節:常量、變量與C語言的數據類型
    在C語言中常量分為:整型常量、實型常量、字符常量(普通字符、轉義字符)、字符串常量、符號常量。符號常量:用#define指令指定用一個符號名稱代表一個常量。例如:#define P 3.14使用符號常量,編譯系統在進行預編譯後,符號常量P已經全部變為字面常量3.14了。2.變量變量代表一個有名字的、具有特定屬性的一個存儲單元。
  • 掌握C語言的必知要點
    溫故而知新,可以為師矣,初學一門語言的時候,我們會躍躍欲試,並沒有真正深入的理解,經過一段時間的實踐,會產生困惑,學而不思則殆,這時回過頭來看書,會有意想不到的收穫,會豁然開朗,會讓你在以後的實踐中更加運用自如
  • C語言編程核心要點
    類型C是強類型語言,有short、long、int、char、float、double等build-in數據類型,類型是貫穿c語言整個課程的核心概念。struct、union、enum屬於c的構造類型,用於自定義類型,擴充類型系統。變量變量用來保存數據,數據是操作的對象,變量的變字意味著它可以在運行時被修改。
  • Js變量:var,let,const三個關鍵字的區別
    var在ECMAScript的所有版本中都可以使用,而const和let只能在ES6及更晚的版本中使用。var,let,const三個關鍵字的區別var:1)聲明作用域:在函數內部,使用var定義一個變量(局部變量),在函數被調用完之後,該變量會被立即銷毀。
  • C語言中變量的命名規則是什麼?
    在C語言中,變量的命名是有明確規則的:1、只能由字母、數字、下劃線組成;2、第一個字符必須是英文字母;3、有效長度為255個字符;>4、不可以包含標點符號和類型說明符(%、&、!、#、@、$); 5、不可以是關鍵詞。