C++/C的內存分配

1、概述

在調試C++/C程序時,經常會遇到這樣的現象,程序運行到某個函數時突然奔潰;使用New或者malloc函數分配一段內存空間時,程序運行一段時間出現奔潰現象。總之,我們經常遇到程序奔潰的問題,而這些問題有時可能也把我們自己搞得很奔潰了。但如果我們能夠了解基本的堆和棧的概念,或許可以避免那些奔潰的瞬間吧。

2、內存分配

段式內存管理架構

常見系統中,都是採用段式內存管理架構。這種架構將內存空間分爲:代碼段(text/code)、數據段(data)和BSS段(bss)。

2.1、代碼段(text segment)

2.1.1、概述

代碼段主要是指存放程序執行代碼的一塊內存區域。這部分區域所要佔用的大小在程序運行前就已經確定,並且該內存區域通常屬於只讀。在代碼段中,也有可能包含一些只讀的常量變量。

2.1.2、program

用戶程序存放在代碼段中,並且一般該區域的屬性只具有隻讀屬性,某些架構下,也允許代碼段爲可寫,及允許修改程序。

2.1.3、rodata

該段一般屬於常量區,用於存放常量數據。rodata全稱read only data,及指只讀數據。所以一般將常量數據存放到該區域,但不是所有的常量都存放在rodata段,有的數據直接放在代碼段中。

  • 常量不一定都放在rodata區域,有的立即數會直接編碼在指令裏,存放在代碼段(.text)中。
  • 對於字符串常量,編譯器會自動去掉重複的字符串,保證一個字符串在一個可執行文件中只存在一份拷貝。
  • rodata是在多個進程間是共享的,這可以提高空間利用率
  • 在有的嵌入式系統中,rodata放在ROM裏,運行時直接讀取ROM內存,無需要加載到內存空間裏

由此可見,在rodata區域的數據,可以在對各進程間共享,可以大大提高空間利用率,甚至可以不佔用RAM空間。另外由於rodata是在只讀的內存頁面中,是受保護的,任何修改其值的操作都會被及時發現,這樣可以幫助提高程序的穩定性。

2.2、BSS段(bss segment)

2.2.1、概述

bss段通常用來存放程序中未初始化初始化爲0全局變量靜態局部變量

2.2.1、bss(Block Started by Symbol)

bss段屬於靜態內存分配,空間結構類似於stack。該區域的數據不會影響最終輸出文件大小。由於是未初始化的變量,編譯器生成輸出文件時,就不需要記錄初始值,只需記錄分配的首末地址即可。同樣對於初始值爲0的變量,由於初始值已知,所以也不需要在輸出文件中記錄,只要在加載時,系統統一賦值0即可。
在大多數系統中,注意不是全部,在加載程序時,會把所有的bss段的變量全部清零,無需你手動去清零。但是像我之前接觸過的TI的C2000系列處理器並不會幫你做上述的操作。所以,爲了保證程序的可移植性,手動把這些變量初始化爲0是比較好的編碼習慣。

2.3、數據段(data segment)

2.3.1、概述

數據段主要用來存儲程序中的初始化的且不爲0全局變量靜態變量,以及提供可供系統動態分配或者用戶程序申請的內存空間。

2.3.2、靜態數據(static data)

靜態數據段屬於靜態內存分配,比較佔文件空間和運行時的內存空間。

2.3.3、堆(heep)

的內存申請和釋放都需要程序去控制,容易產生memory leak,生長方向向上。一般在C++中使用new操作符去申請內存空間,使用delete去釋放相應的內存。這塊區域的內存空間的釋放系統不會去管,需應用程序自己去管理。所以當我們使用new去申請內存空間時,一定要注意使用delete去釋放對應的內存空間,否則容易導致內存的泄漏。

2.3.4、棧(stack)

的內存申請和釋放由系統去管理,生長方向向下,及向着內存地址減小的方向增長。主要存放函數中的局部變量,以及函數調用時,其參數也會被壓入發起調用的進程棧中,並且待到函數調用結束時,函數的返回值也會被存放會棧中。由於棧的先進後出的結構特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把棧看成一個寄存、交換零時數據的內存區。

2.3.4、堆(heep)和棧(stack)的區別

對比
管理方式 用戶管理 系統管理
空間大小 幾乎無限制 有限制
碎片產生
生長方向 向上 向下
分配方式 動態 靜態和動態
分配效率

參考文獻

1、bss、data和rodata區別與聯繫
2、數據段、代碼段、堆棧段、BSS段的區別
3、C/C++內存管理詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章