由c語言**(文字檔案)形成可執行程式(二進位制檔案),需要經過編譯-彙編-鏈結三個階段。編譯過程把c語言文字檔案生成匯程式設計序,彙編過程把匯程式設計序形成二進位制機器**,鏈結過程則將各個原始檔生成的二進位制機器**檔案組合成乙個檔案。
c語言編寫的程式經過編譯-連線後,將形成乙個統一格式的二進位制可執行檔案,這個格式是乙個依照可執行檔案格式的,可以被系統識別,並且載入到記憶體中執行的,它由幾個部分組成。在程式執行時又會產生其他幾個部分,各個部分代表了不同的儲存區域:
乙個程式本質上都是由 bss 段、data段、text段三個組成的。
bss段:在採用段式記憶體管理的架構中,bss段(bss segment)通常是指用來存放程式中未初始化的全域性變數的一塊記憶體區域。bss是英文block started by symbol的簡稱。bss段屬於靜態記憶體分配。
資料段:在採用段式記憶體管理的架構中,資料段(data segment)通常是指用來存放程式中已初始化的全域性變數的一塊記憶體區域。資料段屬於靜態記憶體分配。
**段:在採用段式記憶體管理的架構中,**段(text segment)通常是指用來存放程式執行**的一塊記憶體區域。這部分區域的大小在程式執行前就已經確定,並且記憶體區域屬於唯讀。在**段中,也有可能包含一些唯讀的常數變數,例如字串常量等。
如下圖:
.text即為**段,為唯讀。
.bss段包含程式中未初始化的全域性變數和static變數。
data段包含三個部分:heap(堆)、stack(棧)和靜態資料區。
堆(heap):堆是用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減。當程序呼叫malloc等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);當利用free等函式釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)
棧 (stack):棧又稱堆疊, 是使用者存放程式臨時建立的區域性變數,也就是說我們函式括弧「{}」中定義的變數(但不包括static宣告的變數,static意味著在資料段中存放變 量)。除此以外,在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。由於棧的先進先出特點,所以 棧特別方便用來儲存/恢復呼叫現場。從這個意義上講,我們可以把堆疊看成乙個寄存、交換臨時資料的記憶體區。
靜態資料區存放的是程式中已初始化的全域性變數、靜態變數和常量。
int a = 0; //全域性初始化區,data段
static
int b=20; //全域性初始化區,data段
char *p1; //全域性未初始化區 .bss段
const
int a = 10; //.rodata段
void main(void)
在這裡有幾點需要注意:1. 局變數和靜態變數的儲存是放在一塊的,
初始化的全域性變數和靜態變數在一塊區域(rw data),
未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域(bss)。
程式結束後由系統釋放
2. 對於常量我們需要注意的問題在於,他們並不像我們期望的那樣儲存在常量區(ro data),常量區只用於儲存初始化好的全域性常量以及字串變數本身(不是指標)
區域性常量作為區域性量仍然儲存與棧中
因為常量區與**段是在一起的(在有些段分類結果中,是不存在常量區的,常量區和**段合成為**區)
而本身來說常量只是限制了其讀寫許可權,這種讀寫許可權的限制可以在編譯階段由編譯器進行制定和限制,
這樣在嚴格的編譯器審查結果下,執行階段的**就不存在對常量的讀寫操作,因此就沒必要將其他區域性常量也儲存在常量區 否則將造成**段的臃腫。
討論c/c++中的記憶體布局,不得不提的是資料的儲存類別!資料在記憶體中的位置取決於它的儲存類別。乙個物件是記憶體的乙個位置,解析這個物件依賴於兩個屬性:儲存類別、資料型別。
① 儲存類別決定物件在記憶體中的生命週期。
② 資料型別決定物件值的意義,在記憶體中佔多大空間。
c/c++中由(auto、 extern、 register、 static)儲存類別和物件宣告的上下文決定它的儲存類別。
自動物件(automatic objects)
auto和register將宣告的物件指定為自動儲存類別。他們的作用域是區域性的,諸如乙個函式內,乙個**塊內等。操作了作用域,物件會被銷毀。
在乙個**塊中宣告乙個物件,如果沒有執行auto,那麼預設是自動儲存類別。
宣告為register的物件是自動儲存類別,儲存在計算機的快速暫存器中。不可以對register物件做取值操作「&」。
靜態物件(static objects)
靜態物件可以區域性的,也可以是全域性的。靜態物件一直保持它的值,例如進入乙個函式,函式中的靜態物件仍保持上次呼叫時的值。包含靜態物件的函式不是執行緒安全的、不可重入的,正是因為它具有「記憶」功能。
區域性物件宣告為靜態之後,將改變它在記憶體中儲存的位置,由動態資料--->靜態資料,即從堆或棧變為資料段或bbs段。
全域性物件宣告為靜態之後,而不會改變它在記憶體中儲存的位置,仍然是在資料段或bbs段。但是static將改變它的作用域,即該物件僅在本原始檔有效。此相反的關鍵字是extern,使用extern修飾或者什麼都不帶的全域性物件的作用域是整個程式。
C程式記憶體布局
對於初學c語言學者,理解c程記憶體布局對於我們理解與運用指標非常重要,在筆試中也經常有考題涉及到。以下測試在虛擬機器ubuntu18.04下的測試結果,在樹莓派 arm處理器 測試不同之處也會指出。1.程式各段記憶體遵循上圖順序記憶體布局 結果如下圖 把位址在圖上標出 圖中最低位址和最高位址只是表明...
c 程式的記憶體布局
對任何乙個普通c 程式來講,它都會涉及到 5種不同的資料段。常用的幾個資料段種包含有 程式 段 程式資料段 程式堆疊段 等。不錯,這幾種資料段都在其中,但除了以上幾種資料段之外,程序還另外包含兩種資料段。下面我們來簡單歸納一下程序對應的記憶體空間中所包含的 5種不同的資料區。段 段是用來存放可執行檔...
C程式的記憶體布局
c程式的記憶體布局 c程式的典型記憶體表示由以下部分組成 1.文字段 cpu 執行的機器指令 2.初始化資料段 資料段 3.未初始化的資料段 也稱bss段 4.棧 自動變數以及每次函式呼叫時所需儲存的的資訊都放在棧中 5.堆 通常在堆中進行動態儲存分配 如下圖所示 測試 include includ...