[轉]C程序內存區域分配(5個段作用)


 

參考:
http://www.360doc.com/content/11/0330/21/1317564_105977930.shtml
http://hi.baidu.com/bitcore/blog/item/77c521c65f4512d7d10060eb.html
http://apps.hi.baidu.com/share/detail/22734757
http://my.oschina.net/alphajay/blog/3870

一、C語言可執行代碼結構
 名稱 內容
代碼段  可執行代碼、字符串常量
數據段  已初始化全局變量、已初始化全局靜態變量、局部靜態變量、常量數據
BSS段  未初始化全局變量,未初始化全局靜態變量
 局部變量、函數參數
 動態內存分配

        一般情況下,一個可執行二進制程序(更確切的說,在Linux操作系統下為一個進程單元,在UC/OSII中被稱為任務)在存儲(沒有調入到內存運行)時擁有3個部分,分別是代碼段(text)、數據段(data)和BSS段。這3個部分一起組成了該可執行程序的文件。

        (1)代碼段(text segment):存放CPU執行的機器指令。通常代碼段是可共享的,這使得需要頻繁被執行的程序只需要在內存中擁有一份拷貝即可。代碼段也通常是只讀的,這樣可以防止其他程序意外地修改其指令。另外,代碼段還規划了局部數據所申請的內存空間信息。

        代碼段(code segment/text segment)通常是指用來存放程序執行代碼的一塊內存區域。這部分區域的大小在程序運行前就已經確定,並且內存區域通常屬於只讀, 某些架構也允許代碼段為可寫,即允許修改程序。在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等。

        (2)數據段(data segment):或稱全局初始化數據段/靜態數據段(initialized data segment/data segment)。該段包含了在程序中明確被初始化的全局變量、靜態變量(包括全局靜態變量和局部靜態變量)和常量數據。

        (3)未初始化數據段:亦稱BSS(Block Started by Symbol)。該段存入的是全局未初始化變量、靜態未初始化變量。

        而當程序被加載到內存單元時,則需要另外兩個域:堆域和棧域。圖1-1所示為可執行代碼存儲態和運行態的結構對照圖。一個正在運行的C程序占用的內存區域分為代碼段、初始化數據段、未初始化數據段(BSS)、堆、棧5個部分。


C語言中的5個段作用 - myswirl - 漩渦的窩

圖1-1  C語言可執行代碼結構

      (4)棧段(stack):存放函數的參數值、局部變量的值,以及在進行任務切換時存放當前任務的上下文內容。

      (5)堆段(heap):用於動態內存分配,即使用malloc/free系列函數來管理的內存空間。

     在將應用程序加載到內存空間執行時,操作系統負責代碼段、數據段和BSS段的加載,並將在內存中為這些段分配空間。棧段亦由操作系統分配和管理,而不需要程序員顯示地管理;堆段由程序員自己管理,即顯示地申請和釋放空間。

    另外,可執行程序在運行時具有相應的程序屬性。在有操作系統支持時,這些屬性頁由操作系統管理和維護。

二、例子演示,代碼段、數據段和BSS段存儲變量類型

#include <stdio.h>
const int    g_A       = 10;            //代碼段
int            g_B       = 20;            //數據段
static int    g_C       = 30;            //數據段
static int    g_D;                    //BSS段
int            g_E;                    //BSS段
char        *p1;                    //BSS段

void main( )
{
    int            local_A;            //棧
    static int    local_C = 0;        //數據段
    static int    local_D;            //數據段
    
    char        *p3 = "123456";     //123456在代碼段,p3在棧上

    p1 = (char *)malloc( 10 );      //堆,分配得來得10字節的區域在堆區
    strcpy( p1, "123456" );         //123456{post.content}放在常量區,編譯器可能會將它與p3所指向 的"123456"優化成一塊
    printf("\n");
    printf( "代碼段,全局初始化變量, 只讀const,  g_A,     addr:0x%08x\n", &g_A);
    printf("\n");
    printf( "數據段,全局變量,       初始化      g_B,     addr:0x%08x\n", &g_B);
    printf( "數據段,靜態全局變量,   初始化,     g_C,     addr:0x%08x\n", &g_C);
    printf("\n");
    printf( "BSS段, 全局變量,       未初始化    g_E,     addr:0x%08x\n", &g_E, g_E );    
    printf( "BSS段, 靜態全局變量,   未初始化,   g_D,     addr:0x%08x\n", &g_D );
    printf( "BSS段, 靜態局部變量,   初始化,     local_C, addr:0x%08x\n", &local_C);
    printf( "BSS段, 靜態局部變量,   未初始化,   local_D, addr:0x%08x\n", &local_D);
    printf("\n");
    printf( "棧,    局部變量,                   local_A, addr:0x%08x\n", &local_A );
    printf("\n");
    printf( "堆,    malloc分配內存,             p1,      addr:0x%08x\n", p1 );
}

C程序內存區域分配(5個段作用) - myswirl - 漩渦的窩
注意:
編譯時需要-g選項,這樣才可以看elf信息;
readelf -a MemoryAssign > 1.txt
可執行程序MemoryAssign的信息導出到文本文件1.txt中,查看1.txt 
C程序內存區域分配(5個段作用) - myswirl - 漩渦的窩
 
C程序內存區域分配(5個段作用) - myswirl - 漩渦的窩
 

問題1:可執行文件大小由什么決定?

可執行文件在存儲時分為代碼段、數據段和BSS段三個部分。

【例一】
程序1:
int ar[30000];
void main()
{
    ......

程序2:
int ar[300000] =  {1, 2, 3, 4, 5, 6 };
void main()
{
    ......

發現程序2編譯之后所得的.exe文件比程序1的要大得多。當下甚為不解,於是手工編譯了一下,並使用了/FAs編譯選項來查看了一下其各自的.asm,發現在程序1.asm中ar的定義如下:
_BSS SEGMENT
     ?ar@@3PAHA DD 0493e0H DUP (?)    ; ar
_BSS ENDS 
而在程序2.asm中,ar被定義為:
_DATA SEGMENT
     ?ar@@3PAHA DD 01H     ; ar
                DD 02H
                DD 03H
                ORG $+1199988
_DATA ENDS 
區別很明顯,一個位於.bss段,而另一個位於.data段,兩者的區別在於:全局的未初始化變量存在於.bss段中,具體體現為一個占位符;全局的已初始化變量存於.data段中;而函數內的自動變量都在棧上分配空間。

.bss是不占用.exe文件空間的,其內容由操作系統初始化(清零);而.data卻需要占用,其內容由程序初始化,因此造成了上述情況。

C程序內存區域分配(5個段作用) - myswirl - 漩渦的窩
可以看到可執行文件“2”大小為122K,可執行文件“1”大小為4.8K,用size命令查看二進制可執行文件結構情況。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM