c++内存分布
出處:http://www.cnblogs.com/skynet/
為什么需要知道C/C++的內(nèi)存布局和在哪可以可以找到想要的數(shù)據(jù)?知道內(nèi)存布局對(duì)調(diào)試程序非常有幫助,可以知道程序執(zhí)行時(shí),到底做了什么,有助于寫出干凈的代碼。本文的主要內(nèi)容如下:
- 源文件轉(zhuǎn)換為可執(zhí)行文件
- 可執(zhí)行程序組成及內(nèi)存布局
- 數(shù)據(jù)存儲(chǔ)類別
- 一個(gè)實(shí)例
- 總結(jié)
源文件轉(zhuǎn)換為可執(zhí)行文件
源文件經(jīng)過以下幾步生成可執(zhí)行文件:
- 1、預(yù)處理(preprocessor):對(duì)#include、#define、#ifdef/#endif、#ifndef/#endif等進(jìn)行處理
- 2、編譯(compiler):將源碼編譯為匯編代碼
- 3、匯編(assembler):將匯編代碼匯編為目標(biāo)代碼
- 4、鏈接(linker):將目標(biāo)代碼鏈接為可執(zhí)行文件
編譯器和匯編器創(chuàng)建的目標(biāo)文件包含:二進(jìn)制代碼(指令)、源碼中的數(shù)據(jù);鏈接器將多個(gè)目標(biāo)文件鏈接成一個(gè);裝載器吧目標(biāo)文件加載到內(nèi)存。
圖1 源文件到可執(zhí)行文件的步驟
?
可執(zhí)行程序組成及內(nèi)存布局
通過上面的小節(jié),我們知道將源程序轉(zhuǎn)換為可執(zhí)行程序的步驟,典型的可執(zhí)行文件分為兩部分:
- 代碼段(Code),由機(jī)器指令組成,該部分是不可改的,編譯之后就不再改變,放置在文本段(.text)。
- 數(shù)據(jù)段(Data),它由以下幾部分組:
- 常量(constant),通常放置在只讀read-only的文本段(.text)
- 靜態(tài)數(shù)據(jù)(static data),初始化的放置在數(shù)據(jù)段(.data);未初始化的放置在(.bss,Block Started by Symbol,BSS段的變量只有名稱和大小卻沒有值)
- 動(dòng)態(tài)數(shù)據(jù)(dynamic data),這些數(shù)據(jù)存儲(chǔ)在堆(heap)或棧(stack)
源程序編譯后鏈接到一個(gè)以0地址為始地址的線性或多維虛擬地址空間。而且每個(gè)進(jìn)程都擁有這樣一個(gè)空間,每個(gè)指令和數(shù)據(jù)都在這個(gè)虛擬地址空間擁有確定的地址,把這個(gè)地址稱為虛擬地址(Virtual Address)。將進(jìn)程中的目標(biāo)代碼、數(shù)據(jù)等的虛擬地址組成的虛擬空間稱為虛擬存儲(chǔ)器(Virtual Memory)。典型的虛擬存儲(chǔ)器中有類似的布局:
- Text Segment (.text)
- Initialized Data Segment (.data)
- Uninitialized Data Segment (.bss)
- The Stack
- The Heap
如下圖所示:
圖2 進(jìn)程內(nèi)存布局
當(dāng)進(jìn)程被創(chuàng)建時(shí),內(nèi)核為其提供一塊物理內(nèi)存,將虛擬內(nèi)存映射到物理內(nèi)存,這些都是由操作系統(tǒng)來做的。
數(shù)據(jù)存儲(chǔ)類別
討論C/C++中的內(nèi)存布局,不得不提的是數(shù)據(jù)的存儲(chǔ)類別!數(shù)據(jù)在內(nèi)存中的位置取決于它的存儲(chǔ)類別。一個(gè)對(duì)象是內(nèi)存的一個(gè)位置,解析這個(gè)對(duì)象依賴于兩個(gè)屬性:存儲(chǔ)類別、數(shù)據(jù)類型。
- 存儲(chǔ)類別決定對(duì)象在內(nèi)存中的生命周期。
- 數(shù)據(jù)類型決定對(duì)象值的意義,在內(nèi)存中占多大空間。
C/C++中由(auto、 extern、 register、 static)存儲(chǔ)類別和對(duì)象聲明的上下文決定它的存儲(chǔ)類別。
1、自動(dòng)對(duì)象(automatic objects)
auto和register將聲明的對(duì)象指定為自動(dòng)存儲(chǔ)類別。他們的作用域是局部的,諸如一個(gè)函數(shù)內(nèi),一個(gè)代碼塊{***}內(nèi)等。操作了作用域,對(duì)象會(huì)被銷毀。
- 在一個(gè)代碼塊中聲明一個(gè)對(duì)象,如果沒有執(zhí)行auto,那么默認(rèn)是自動(dòng)存儲(chǔ)類別。
- 聲明為register的對(duì)象是自動(dòng)存儲(chǔ)類別,存儲(chǔ)在計(jì)算機(jī)的快速寄存器中。不可以對(duì)register對(duì)象做取值操作“&”。
2、靜態(tài)對(duì)象(static objects)
靜態(tài)對(duì)象可以局部的,也可以是全局的。靜態(tài)對(duì)象一直保持它的值,例如進(jìn)入一個(gè)函數(shù),函數(shù)中的靜態(tài)對(duì)象仍保持上次調(diào)用時(shí)的值。包含靜態(tài)對(duì)象的函數(shù)不是線程安全的、不可重入的,正是因?yàn)樗哂小坝洃洝惫δ堋?/p>
- 局部對(duì)象聲明為靜態(tài)之后,將改變它在內(nèi)存中保存的位置,由動(dòng)態(tài)數(shù)據(jù)--->靜態(tài)數(shù)據(jù),即從堆或棧變?yōu)閿?shù)據(jù)段或bbs段。
- 全局對(duì)象聲明為靜態(tài)之后,而不會(huì)改變它在內(nèi)存中保存的位置,仍然是在數(shù)據(jù)段或bbs段。但是static將改變它的作用域,即該對(duì)象僅在本源文件有效。此相反的關(guān)鍵字是extern,使用extern修飾或者什么都不帶的全局對(duì)象的作用域是整個(gè)程序。
?
一個(gè)實(shí)例
下面我們分析一段代碼:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> #include <stdlib.h> int a; static int b; void func( void ) { ????char c; ????static int d; } int main( void ) { ????int e; ????int *pi = ( int *) malloc ( sizeof ( int )); ????func (); ????func (); ????free (pi ); ????return (0); } |
程序中聲明的變量a、b、c、d、e、pi的存儲(chǔ)類別和生命期如下所述:
- a是一個(gè)未初始化的全局變量,作用域?yàn)檎麄€(gè)程序,生命期是整個(gè)程序運(yùn)行期間,在內(nèi)存的bbs段
- b是一個(gè)未初始化的靜態(tài)全局變量,作用域?yàn)楸驹次募?#xff0c;生命期是整個(gè)程序運(yùn)行期間,在內(nèi)存的bbs段
- c是一個(gè)未初始化的局部變量,作用域?yàn)楹瘮?shù)func體內(nèi),即僅在函數(shù)體內(nèi)可見,生命期也是函數(shù)體內(nèi),在內(nèi)存的棧中
- d是一個(gè)未初始化的靜態(tài)局部變量,作用域?yàn)楹瘮?shù)func體內(nèi),即僅在函數(shù)體內(nèi)可見,生命期是整個(gè)程序運(yùn)行期間,在內(nèi)存的bbs段
- e是一個(gè)未初始化的局部變量,作用域?yàn)楹瘮?shù)main體內(nèi),即僅在函數(shù)體內(nèi)可見,生命期是main函數(shù)內(nèi),在內(nèi)存的棧中
- pi是一個(gè)局部指針,指向堆中的一塊內(nèi)存塊,該塊的大小為sizeof(int),pi本身存儲(chǔ)在內(nèi)存的棧中,生命期是main函數(shù)內(nèi)
- 新申請(qǐng)的內(nèi)存塊在堆中,生命期是malloc/free之間
用圖表示如下:
圖3 例子的內(nèi)存布局
?
總結(jié)
本文介紹了C/C++中由源程序到可執(zhí)行文件的步驟,和可執(zhí)行程序的內(nèi)存布局,數(shù)據(jù)存儲(chǔ)類別,最后還通過一個(gè)例子來說明。可執(zhí)行程序中的變量在內(nèi)存中的布局可以總結(jié)為如下:
- 變量(函數(shù)外):如果未初始化,則存放在BSS段;否則存放在data段
- 變量(函數(shù)內(nèi)):如果沒有指定static修飾符,則存放在棧中;否則同上
- 常量:存放在文本段.text
- 函數(shù)參數(shù):存放在棧或寄存器中
內(nèi)存可以分為以下幾段:
- 文本段:包含實(shí)際要執(zhí)行的代碼(機(jī)器指令)和常量。它通常是共享的,多個(gè)實(shí)例之間共享文本段。文本段是不可修改的。
- 初始化數(shù)據(jù)段:包含程序已經(jīng)初始化的全局變量,.data。
- 未初始化數(shù)據(jù)段:包含程序未初始化的全局變量,.bbs。該段中的變量在執(zhí)行之前初始化為0或NULL。
- 棧:由系統(tǒng)管理,由高地址向低地址擴(kuò)展。
- 堆:動(dòng)態(tài)內(nèi)存,由用戶管理。通過malloc/alloc/realloc、new/new[]申請(qǐng)空間,通過free、delete/delete[]釋放所申請(qǐng)的空間。由低地址想高地址擴(kuò)展。
總結(jié)
- 上一篇: linux多线程基本编程
- 下一篇: C++成员函数的内存分配问题