為什麼需要知道C/C++的內存布局和在哪可以可以找到想要的數據?知道內存布局對調試程序非常有幫助,可以知道程序執行時,到底做了什麼,有助於寫出干淨的代碼。本文的主要內容如下:
源文件經過以下幾步生成可執行文件:
編譯器和匯編器創建的目標文件包含:二進制代碼(指令)、源碼中的數據;鏈接器將多個目標文件鏈接成一個;裝載器吧目標文件加載到內存。
圖1 源文件到可執行文件的步驟
通過上面的小節,我們知道將源程序轉換為可執行程序的步驟,典型的可執行文件分為兩部分:
源程序編譯後鏈接到一個以0地址為始地址的線性或多維虛擬地址空間。而且每個進程都擁有這樣一個空間,每個指令和數據都在這個虛擬地址空間擁有確定的地址,把這個地址稱為虛擬地址(Virtual Address)。將進程中的目標代碼、數據等的虛擬地址組成的虛擬空間稱為虛擬存儲器(Virtual Memory)。典型的虛擬存儲器中有類似的布局:
圖2 進程內存布局
當進程被創建時,內核為其提供一塊物理內存,將虛擬內存映射到物理內存,這些都是由操作系統來做的。
討論C/C++中的內存布局,不得不提的是數據的存儲類別!數據在內存中的位置取決於它的存儲類別。一個對象是內存的一個位置,解析這個對象依賴於兩個屬性:存儲類別、數據類型。
C/C++中由(auto、 extern、 register、 static)存儲類別和對象聲明的上下文決定它的存儲類別。
auto和register將聲明的對象指定為自動存儲類別。他們的作用域是局部的,諸如一個函數內,一個代碼塊{***}內等。操作了作用域,對象會被銷毀。
靜態對象可以局部的,也可以是全局的。靜態對象一直保持它的值,例如進入一個函數,函數中的靜態對象仍保持上次調用時的值。包含靜態對象的函數不是線程安全的、不可重入的,正是因為它具有“記憶”功能。
下面我們分析一段代碼:
程序中聲明的變量a、b、c、d、e、pi的存儲類別和生命期如下所述:
用圖表示如下:
圖3 例子的內存布局
本文介紹了C/C++中由源程序到可執行文件的步驟,和可執行程序的內存布局,數據存儲類別,最後還通過一個例子來說明。可執行程序中的變量在內存中的布局可以總結為如下:
內存可以分為以下幾段: