C 記憶體布局詳解

2022-08-16 13:09:09 字數 2356 閱讀 4464

乙個由c/c++編譯的程式除了存放函式二進位制**的程式**段(code段)外,資料占用的記憶體大致分為以下幾個部分:

1、棧區(stack)

存放區域性變數、函式引數、返回資料、返回位址等。系統自動分配釋放,其操作方式類似於資料結構中的棧。

需要注意的有三點:

第一,退棧後銷毀進棧時定義資料,請看下面這段。

char*fun(char *p)

//下面是某呼叫函式中的**

char *a ="

world";

printf(

"%s\n

",fun(a));

輸出的結果是什麼?hello還是world?

test陣列是區域性變數,當所屬函式被呼叫時,系統為之在棧區分配一段記憶體空間,存六個字元以及一些記錄資訊,test對應該段記憶體空間首位址,當乙個引數即指標p傳入時,將test的值賦給p,而當函式呼叫結束的時候,發生退棧操作,剛才分配的記憶體被「銷毀」,即寫入「垃圾資訊」。所以呼叫的結果是:給引數賦給了乙個棧區位址,該位址指向的記憶體空間裡是一堆垃圾資訊,所以輸出結果既不是hello又不是world。

第二,正是由於棧「後進先出」的特點,所以函式呼叫的機制是借助棧區來完成的,而當我們進行大量頻繁的呼叫操作時,系統將隨之進行大量的進棧退棧操作,從而消耗時間資源,使得程式的執行效率下降。c語言解決的方法是使用巨集來代替那些短小而被頻繁呼叫的函式,而c++則是引入了內聯函式機制。除此之外,還有就是提高程式設計技巧,注意細節,比如這段**,for (i=0; i如果迴圈過程不改變fun()的取值的話,那麼強烈建議將此**調整為

temp =fun();

for (i=0

;i

由於減少了每次迴圈中呼叫函式所造成的進棧退棧開銷,所以執行效率將大大提高。需要模擬的乙個例子是迴圈巢狀,同樣為了降低開銷,建議在允許的情況下,將迴圈次數多的迴圈放在裡面。

第三,由於系統為棧分配的空間很有限,一般只有1m(可以調整設定),如果申請的棧空間太大,將會出現棧溢位的錯誤。一次試驗時,我用的機器上能分配的最大棧空間為1036084byte,也就是0.988m。所以當在棧區定義「大資料」時,一定要敏感。

2、堆區(heap)

堆:是在任何記憶體中動態和隨機分配的記憶體的統稱,也就是無序的,

由程式設計師通過malloc()函式(free釋放),new操作符分配(delete/delete釋放),若程式設計師不釋放,程式結束時可能由os**。分配方式類似於鍊錶。

需要注意的有五點:

第一,分配後馬上進行分配成功與否的驗證。

第二,有分配必須有釋放,否則將有可能造成記憶體洩露。遵循「誰分配誰釋放」的原則。

第三,操作上,malloc和free對應,new和delete對應。另外,前者是庫函式,使用時需要載入相應標頭檔案,後者是c++中的關鍵字,是運算子,不需要加特別的標頭檔案。

第四,執行效率沒有棧高,而且大量頻繁使用將造成更多的記憶體碎片。

第五,雖然free()函式的引數是指標,但它釋放的是記憶體而不是指標,所以當執行完free()操作後,還應該將指標置空,以避免野指標問題。

3、全域性區(靜態區)(static)

存放全域性變數、靜態變數。程式結束後由系統釋放。

c程式根據全域性變數和靜態變數有沒有進行顯式初始化,還將它們分為兩個不同的區域,即bss和data。據說這種區別在c++中已經沒有了,而且編碼規範要求我們在定義乙個變數時,一定要同時對它進行初始化(尤其是乙個指標,最好將它置為空),所以我們盡量還是在允許的情況下,遵守這一規範。另外需要特別說明的是,當沒有進行顯式初始化時,它們的值將被初始化為0。

當函式或外部變數的前面冠以static時,它們的可見範圍將限定在所在檔案內,程式中其他檔案無法見到它們,我們可以用這個辦法來有效避免命名衝突。而當乙個區域性變數被static修飾時,它的可見範圍並沒有修改,還是限定在函式內,但它的生存期將延長為程式生存期,因為儲存區域不是棧區,所以不會因為退棧而被

char *p ="

atbcdef";

*(p+1) ='

b';

銷毀。4、文字常量區

常量字串就是放在這裡的。程式結束後由系統釋放。

常量,顧名思義,不可進行寫操作,關於這一點的注意事項,請看下面這段錯誤**;

char *p ="

atbcdef";

*(p+1) ='

b';

程式執行時將報非法寫入錯誤。

關於區域性的字串常量是存放在全域性的常量區還是棧區,不同的編譯器有不同的實現。可以通過組合語言檢視一下。vc環境下,區域性常量就像區域性變數一樣儲存於棧中,全域性常量、字元常量儲存於文字常量區。tc在常量區。

C 記憶體布局

注意,上述只描述的是可執行檔案具有三個段,而不是由該三個段構成。在 linux 下,我們可以通過size命令輸出可執行檔案的段資訊。記憶體布局 存放程式指令和字串常量 我們知道,可執行檔案的文字段包含程式的指令,鏈結器把指令直接從可執行檔案拷貝到記憶體中,形成文字段。存放已初始化的全域性變數和sta...

c 記憶體布局

寫好了 只是第一步,接下來還需要編譯生成對應的二進位制才能使用 預處理,編譯,彙編,鏈結 那麼在執行的時候,和資料在記憶體中都是怎麼分布的呢?c的記憶體布局是怎樣的呢?c 的記憶體布局是怎樣的呢?有一點值得注意,c語言和c 的記憶體布局是不一樣的,這也就是平日裡搜尋c 記憶體布局的文章內容總是很相似...

C語言記憶體布局

重點關注以下內容 c語言程式在記憶體中各個段的組成 c語言程式連線過程中的特性和常見錯誤 c語言程式的執行方式 一 c語言程式的儲存區域 由c語言 文字檔案 形成可執行程式 二進位制檔案 需要經過編譯 彙編 連線三個階段。編譯過程把c語言文字檔案生成匯程式設計序,彙編過程把匯程式設計序形成二進位制機...