c/c++程序的內存分區模型詳解

內存分區

運行之前

我們要想執行我們編寫的c程序,那麼第一步需要對這個程序進行編譯。

  1. 預處理:宏定義展開、頭文件展開、條件編譯,這裏並不會檢查語法
  2. 編譯:檢查語法,將預處理後文件編譯生成彙編文件
  3. 彙編:將彙編文件生成目標文件(二進制文件)
  4. 鏈接:將目標文件鏈接爲可執行程序

 

在沒有運行程序前,也就是說程序沒有加載到內存前,可執行程序內部已經分好3段信息,分別爲代碼區(text)、數據區(data)和未初始化數據區(bss)3 個部分(有些人直接把data和bss合起來叫做靜態區或全局區)

  • 代碼區

存放 CPU 執行的機器指令。通常代碼區是可共享的(即另外的執行程序可以調用它),使其可共享的目的是對於頻繁被執行的程序,只需要在內存中有一份代碼即可。代碼區通常是隻讀的,使其只讀的原因是防止程序意外地修改了它的指令。另外,代碼區還規劃了局部變量的相關信息。

 

  • 全局初始化數據區/靜態數據區(data段)

該區包含了在程序中明確被初始化的全局變量、已經初始化的靜態變量(包括全局靜態變量和局部靜態變量)和常量數據(如字符串常量)。

 

  • 未初始化數據區(又叫 bss 區)

存入的是全局未初始化變量和未初始化靜態變量。未初始化數據區的數據在程序開始執行之前被內核初始化爲 0 或者空(NULL)。

總體來講說,程序源代碼被編譯之後主要分成兩種段:程序指令和程序數據。代碼段屬於程序指令,而數據域段和.bss段屬於程序數據。

那爲什麼把程序的指令和程序數據分開呢?

  • 程序被load到內存中之後,可以將數據和代碼分別映射到兩個內存區域。由於數據區域對進程來說是可讀可寫的,而指令區域對程序來講說是隻讀的,所以分區之後呢,可以將程序指令區域和數據區域分別設置成可讀可寫或只讀。這樣可以防止程序的指令有意或者無意被修改。
  • 當系統中運行着多個同樣的程序的時候,這些程序執行的指令都是一樣的,所以只需要內存中保存一份程序的指令就可以了,只是每一個程序運行中數據不一樣而已,這樣可以節省大量的內存。比如說之前的Windows Internet Explorer 7.0運行起來之後, 它需要佔用112 844KB的內存,它的私有部分數據有大概15 944KB,也就是說有96 900KB空間是共享的,如果程序中運行了幾百個這樣的進程,可以想象共享的方法可以節省大量的內存。

運行之後

程序在加載到內存前,代碼區和全局區(data和bss)的大小就是固定的,程序運行期間不能改變。然後,運行可執行程序,操作系統把物理硬盤程序load(加載)到內存,除了根據可執行程序的信息分出代碼區(text)、數據區(data)和未初始化數據區(bss)之外,還額外增加了棧區、堆區

  • 代碼區(text segment)

加載的是可執行文件代碼段,所有的可執行代碼都加載到代碼區,這塊內存是不可以在運行期間修改的。

 

  • 未初始化數據區(BSS)

加載的是可執行文件BSS段,位置可以分開亦可以緊靠數據段,存儲於數據段的數據(全局未初始化,靜態未初始化數據)的生存週期爲整個程序運行過程。

 

  • 全局初始化數據區/靜態數據區(data segment)

加載的是可執行文件數據段,存儲於數據段(全局初始化,靜態初始化數據,文字常量(只讀))的數據的生存週期爲整個程序運行過程。

 

  • 棧區(stack)

棧是一種先進後出的內存結構,由編譯器自動分配釋放,存放函數的參數值、返回值、局部變量等。在程序運行過程中實時加載和釋放,因此,局部變量的生存週期爲申請到釋放該段棧空間。

 

  • 堆區(heap)

堆是一個大容器,它的容量要遠遠大於棧,但沒有棧那樣先進後出的順序。用於動態內存分配。堆在內存中位於BSS區和棧區之間。一般由程序員分配和釋放,若程序員不釋放,程序結束時由操作系統回收。

類型

作用域

生命週期

存儲位置

auto變量

一對{}內

當前函數

棧區

static局部變量

一對{}內

整個程序運行期

初始化在data段,未初始化在BSS段

extern變量

整個程序

整個程序運行期

初始化在data段,未初始化在BSS段

static全局變量

當前文件

整個程序運行期

初始化在data段,未初始化在BSS段

extern函數

整個程序

整個程序運行期

代碼區

static函數

當前文件

整個程序運行期

代碼區

register變量

一對{}內

當前函數

運行時存儲在CPU寄存器

字符串常量

當前文件

整個程序運行期

data

總結

在理解C/C++內存分區時,常會碰到如下術語:數據區,堆,棧,靜態區,常量區,全局區,字符串常量區,文字常量區,代碼區等等,初學者被搞得雲裏霧裏。在這裏,嘗試捋清楚以上分區的關係。

       數據區包括:堆,棧,全局/靜態存儲區。
  全局/靜態存儲區包括:常量區,全局區、靜態區。
  常量區包括:字符串常量區、常變量區。
  代碼區:存放程序編譯後的二進制代碼,不可尋址區。

可以說,C/C++內存分區其實只有兩個,即代碼區和數據區。

 

 

 

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