C語言內存分配

C語言內存布局

程序執行的時候一定會佔用內存空間。為了能夠更好地管理內存空間,我們通常要對內存進行布局,將其劃分為不同的功能塊。這有利於提高執行的效率、提高空間利用率、提高代碼的安全性等。

在PC中,操作系統會在RAM中開闢一段連續的內存空間供程序使用。我們把內存空間從低地址位到高地址位,劃分為五大段,如下圖所示。

img

Text(RO): code and constant data

  • 代碼段存放彙編產生的機器代碼。
  • 為了防止數據溢出導致覆蓋代碼,將Text放在最低地址處。
  • 代碼段是可以共享的,數據段是私有的,當運行多個程序的副本時,只需要保存一份代碼段部分。這在現代的操作系統裏面佔據了極為重要的地位,特別是在有動態鏈接的系統中,可以節省大量的內存。

Data(RW): initialized global and static variables

Data段在可執行文件中,由系統從可執行文件中加載。

BSS(RW): uninitialized global and static variables

BSS段中的變量都會被初始化為確定的值0,這些初始化的0不需要存儲在可執行文件中,而只需要在ELF文件頭部的Section Table中說明BSS空間大小,在Symbol Table中說明符號即可。當文件加載運行時,才分配空間以及初始化。因此,BSS段不佔用任何的磁盤空間。

img

Heap(RW): dynamic memory

  • 在程序運行時由編譯器自動分配。
  • 用來存儲函數調用時的臨時信息的結構,如函數調用所傳遞的參數、函數的返回地址、函數的局部變量等。
  • 只是分配空間,並未對其初始化。因此局部變量必須要手動初始化。
  • 連續的地址。
  • 向低地址增長。這樣棧空間的起始位置就能確定下來,動態的調整棧空間大小也不需要移動棧內的數據。

Stack(RW): local variables

  • 在程序運行時由程序員自主分配。
  • 不連續的地址。因為其是使用鏈表來分配的。
  • 向高地址增長。這樣做內存管理相對要簡單些。

嵌入式C語言內存布局

嵌入式的內存是很寶貴的。我們不可能像PC那樣將所有的段全部放在RAM中。而應根據數據的特點,將只讀的部分存入ROM中,將讀寫的部分存入RAM中。

Keil-MDK在編譯後,會將程序分為四個部分。

  • Code: Code in Text
  • RO-data: Constant data in Text
  • RW-data: Data
  • ZI-data: BSS + Heap + Stack

將這四個部分分別存入RAM和ROM中。

  • ROM: Code + RO-data + RW-data
  • RAM: RW-data + ZI-data

ROM中還要存RW,因為掉電後RAM中所有數據都丟失了,每次上電RAM中的數據是被重新賦值的,而這些固定的初始值就是存儲在ROM中的。

ROM中的指令至少應該有這樣的功能:

  • 將RW從ROM中搬到RAM中,因為RW是變量,變量不能存在ROM中。
  • 將ZI所在的RAM區域全部清零,因為ZI區域並不在Image中,所以需要程序根據編譯器給出的ZI地址及大小來將相應得RAM區域清零。ZI中也是變量,同理:變量不能存在ROM中。

keil-MDK實際測試結果

char* 和 char[]

char* s = "string";  //定義了一個指針,指向了字符串常量。存在Text段中。會有warning,因為指針類型是可以改變的,而常量是不能改變的。
const char*s = "string";  //定義了一個常量指針,指向了字符串常量。存在Text段中。
char s[] = "string";  //定義了一個數組,數組裡存儲着字符串。存在Data段中。