Const

ciscopuke發表於2021-09-09

const:限定一個變數不允許改變,產生靜態作用,const在一定程度上可以提高程式的安全性和可靠性。
const 推出的初始目的,正是為了取代預編譯指令,消除它的缺點,同時繼承它的優點。宏定義:

#define xxxx   {
    xxx
}

後不能有任何字元,包括空格  最後的 “}”需要最少空一行

struct xxxx  xxx = {.x1 = xxxxx,.x2 = xxxxx,.x3 = xxxxx};

在C99模式下可以使用該方式對結構體進行初始化,方便觀察每一個變數的初始值。

宣告為const的變數是不能被使用者改變的,因為編譯器會將該變數放在只讀區,比如在KEIL開發平臺下,宣告為const的變數放在FLASH區,這樣即使你使用取地址符 & 獲取宣告為 const 變數地址,並透過指標進行修改,雖然編譯器不報錯,但也是無法進行修改的,因為進行FLASH程式設計是有條件的。


圖片描述

image.png

你會發現雖然P獲取了N的地址,但因為N存放在FLAH中,所以透過指標也是無法改變N的值的。


圖片描述

image.png

可以看到N沒有改變。但是編譯器確實也沒報錯。但是如果你直接 N = 4 的話,肯定是報錯的,因為你的N被申明為const。


圖片描述

image.png

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

圖片描述

image.png


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


圖片描述

image.png

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


圖片描述

image.png

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


圖片描述

image.png

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


圖片描述

image.png

這像一個地址。但透過Memory檢視這個地址發現存放的不是5。


圖片描述

image.png

根據ARM核心的知識可以知道,0x08000000地址存放的其實是棧頂指標,也就是說N存放的是棧頂指標嗎?顯然不是。
然後對.map地址對映檔案進行搜尋,你會發現,根本沒有N的地址。這樣說來,N在記憶體的位置對使用者是不可見的,而是由編譯器自動處理了。
那麼有沒有辦法找到這個複製源頭呢。之前我說過,先前能找到複製的源頭純屬偶然,有沒有什麼方法可以找到呢?這其中的難點就是進入main函式之前的那段複製程式碼不是我們使用者自己寫的,而是C編譯器自動處理的,怎麼辦?
這個時候就需要請出一個關鍵人物:資料觀察點(關於資料觀察點,將有專門的一小節詳細說明)。
我們知道,不管如何,因為FLASH存放著變數初始值,然後在程式執行的時候才將FLASH中的值初始化到RAM中去,也就是我們使用的RAM變數,那麼必然存在透過匯流排進行資料傳輸的過程,所以可以透過資料觀察點的功能實現對地址的監控,雖然我們不知道FLAH的地址,但是我們知道RAM的地址,所以只要對變數i進行監控,就可以透過核心的暫存器找到FLASH地址了。如下:


圖片描述

image.png

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


圖片描述

image.png

由此可以知道,FLAH中也是有多個相同副本存在的。
因此可以得出結論,在STM32、KEIL的環境下,《C語言深度剖析》對於兩者的說法在這裡不適用。
看了那麼多,沒有足夠的基礎是很難知道我在講什麼的,下面用一張圖進行說明,希望可以解答你的疑惑。



作者:EmbeddedOsprey
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4369/viewspace-2820894/,如需轉載,請註明出處,否則將追究法律責任。