C語言中內存分佈及程序運行中(BSS段、數據段、代碼段、堆棧)

BSS段:(bss segment)通常是指用來存放程序中未初始化全局變量的一塊內存區域。BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態內存分配。

數據段 :數據段(data segment)通常是指用來存放程序中 已初始化  全局變量 的一塊內存區域。數據段屬於靜態內存分配。 
代碼段: 代碼段(code segment/text segment)通常是指用來存放 程序執行代碼 的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於 只讀 , 某些架構也允許代碼段爲可寫,即允許修改程序。在代碼段中,也有可能包含一些 只讀的常數變量 ,例如字符串常量等。程序段爲程序代碼在內存中的映射.一個程序可以在內存中多有個副本.

堆(heap) :堆是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc/free等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張)/釋放的內存從堆中被剔除(堆被縮減)

棧(stack) :棧又稱堆棧, 存放程序的 局部變量 (但不包括static聲明的變量, static 意味着 在數據段中 存放變量)。除此以外,在函數被調用時,棧用來傳遞參數和返回值。由於棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場儲動態內存分配,需要程序員手工分配,手工釋放

下圖是APUE中的一個典型C內存空間分佈圖

 例如:

#include <stdio.h>

int g1=0, g2=0, g3=0;

int max(int i)
{
    int m1=0,m2,m3=0,*p_max;
    static n1_max=0,n2_max,n3_max=0;
     p_max = (int*)malloc(10);
    printf("打印max程序地址\n");
    printf("in max: 0x%08x\n\n",max);
    printf("打印max傳入參數地址\n");
    printf("in max: 0x%08x\n\n",&i);
    printf("打印max函數中靜態變量地址\n");
    printf("0x%08x\n",&n1_max); //打印各本地變量的內存地址
    printf("0x%08x\n",&n2_max);
    printf("0x%08x\n\n",&n3_max);
    printf("打印max函數中局部變量地址\n");
    printf("0x%08x\n",&m1); //打印各本地變量的內存地址
    printf("0x%08x\n",&m2);
    printf("0x%08x\n\n",&m3);
    printf("打印max函數中malloc分配地址\n");
    printf("0x%08x\n\n",p_max); //打印各本地變量的內存地址

    if(i) return 1;
    else return 0;
}

int main(int argc, char **argv)
{
    static int s1=0, s2, s3=0;
    int v1=0, v2, v3=0;
    int *p;    
    p = (int*)malloc(10);

    printf("打印各全局變量(已初始化)的內存地址\n");
    printf("0x%08x\n",&g1); //打印各全局變量的內存地址
    printf("0x%08x\n",&g2);
    printf("0x%08x\n\n",&g3);
    printf("======================\n");
    printf("打印程序初始程序main地址\n");
    printf("main: 0x%08x\n\n", main);
    printf("打印主參地址\n");
    printf("argv: 0x%08x\n\n",argv);
    printf("打印各靜態變量的內存地址\n");
    printf("0x%08x\n",&s1); //打印各靜態變量的內存地址
    printf("0x%08x\n",&s2);
    printf("0x%08x\n\n",&s3);
    printf("打印各局部變量的內存地址\n");
    printf("0x%08x\n",&v1); //打印各本地變量的內存地址
    printf("0x%08x\n",&v2);
    printf("0x%08x\n\n",&v3);
    printf("打印malloc分配的堆地址\n");
    printf("malloc: 0x%08x\n\n",p);
    printf("======================\n");
    max(v1);
    printf("======================\n");
    printf("打印子函數起始地址\n");
    printf("max: 0x%08x\n\n",max);
    return 0;
}

 打印結果:

可以大致查看整個程序在內存中的分配情況:
可以看出,傳入的參數,局部變量,都是在棧頂分佈,隨着子函數的增多而向下增長.
函數的調用地址(函數運行代碼),全局變量,靜態變量都是在分配內存的低部存在,而malloc分配的堆則存在於這些內存之上,並向上生長.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在操作系統中,一個進程就是處於執行期的程序(當然包括系統資源),實際上正在執行的程序代碼的活標本。那麼進程的邏輯地址空間是如何劃分的呢?

引用:

 

圖1做了簡單的說明(Linux系統下的)


左邊的是UNIX/LINUX系統的執行文件,右邊是對應進程邏輯地址空間的劃分情況。



首先是堆棧區(stack),堆棧是由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。棧的申請是由系統自動分配,如在函數內部申請一個局部變量 int h,同時判別所申請空間是否小於棧的剩餘空間,如若小於的話,在堆棧中爲其開闢空間,爲程序提供內存,否則將報異常提示棧溢出。    
其次是堆(heap),堆一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表。堆的申請是由程序員自己來操作的,在C中使用malloc函數,而C++中使用new運算符,但是堆的申請過程比較複雜:當系統收到程序的申請時,會遍歷記錄空閒內存地址的鏈表,以求尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,此處應該注意的是有些情況下,新申請的內存塊的首地址記錄本次分配的內存塊大小,這樣在delete尤其是delete[]時就能正確的釋放內存空間。
接着是全局數據區(靜態區) (static),全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 另外文字常量區,常量字符串就是放在這裏,程序結束後有系統釋放。
最後是程序代碼區,放着函數體的二進制代碼。

舉例說明一下:
int a = 0;                   //全局初始化區 


char *p1;                //全局未初始化區 


int main() 

{ 

        int b;                // 棧 

        char s[] = "abc";       //棧 

        char *p2;             //棧 

        char *p3 = "123456";    //123456\0在常量區,而p3在棧上。 

        static int c =0;    //全局(靜態)初始化區 

        p1 = (char *)malloc(10);

        p2 = (char *)malloc(20); //分配得來得10和20字節的區域就在堆區。 

        strcpy(p1, "123456");    //123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。

        return 0;

}

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