1. 程式人生 > >.bss段和.data段的區別

.bss段和.data段的區別

在採用段式記憶體管理的架構中,BSS段(bss segment)通常是指用來存放程式中未初始化全域性變數的一塊記憶體區域。BSS是英文Block Started by Symbol的簡稱。BSS段屬於靜態記憶體分配。(不太清楚這段話是否正確)

【例一】

用cl編譯兩個小程式如下:

程式1:

int ar[30000];
void main()
{
    ......
}


程式2:

int ar[300000] = {1, 2, 3 };
void main()
{
    ......
}


發現程式2編譯之後所得的.exe檔案比程式1的要大得多。當下甚為不解,於是手工編譯了一下,並使用了/FAs編譯選項來查看了一下其各自的.asm,發現在程式1.asm中ar的定義如下:

_BSS SEGMENT
     DD 0493e0H DUP (?)    ; ar
_BSS ENDS


而在程式2.asm中,ar被定義為:

_DATA SEGMENT
     DD 01H     ; ar
                DD 02H
                DD 03H
                ORG $+1199988       //後面會有講解
_DATA ENDS


區別很明顯,一個位於.bss段,而另一個位於.data段,兩者的區別在於:全域性的未初始化變數存在於. bss段中,具體體現為一個佔位符;全域性的已初始化變數存於.data段中;而函式內的自動變數都在棧上分配空間。.bss是不佔用.exe檔案空間的, 其內容由作業系統初始化(清零);而.data卻需要佔用,其內容由程式初始化,因此造成了上述情況。

------------------------
注:為什麼說.bss是不佔用.exe檔案空間的呢?因為.bss段儲存的都是一些沒有初始化的變數,所以就沒有必要把這些變數的值都儲存下來(你儲存下 來也沒有意義,因為沒有經過初始化,裡面的值都是一些隨意的值),只要記錄下需要分配多大的空間就行了。到時候告訴loader,“喂,loader,你 給我分配多少多少個空間啊”,呵呵,好了,這就足夠了,還可以節省不少空間!其實,從把.bss和.data段區分開來對待這一個角度,也可以反映出當初 計算機的設計是多麼的巧妙!!能節省空間就節省!:-)

上面彙編程式碼中的ORG指令你可能不太熟悉,其實這是一條偽指令。作用就是從當前位置跳到哪,$表示 當前段內的偏移量,所以ORG    $+100指令的意思就是,在當前段內,向前跳100個位元組,這樣就可以空出100個位元組的空間。當然,你不要把ORG指令與JMP指令混為一談,它倆根 本不是一回事,一個是彙編器指令,一個是機器指令!:-)

至於上面為什麼是ORG   $+1199988,哪來的1199988??呵呵,自己想去吧,我只提示你一個整數是4個位元組^_^

【例二】

編譯如下程式(test.cpp):
#include <stdio.h>

#define LEN 1002000

int inbss[LEN];
float fA;
int indata[LEN]={1,2,3,4,5,6,7,8,9};
double dbB = 100.0;

const int cst = 100;

int main(void)
{
    int run[100] = {1,2,3,4,5,6,7,8,9};
    for(int i=0; i<LEN; ++i)
        printf("%d ", inbss[i]);
    return 0;
}

命令:cl /FA test.cpp 回車 (/FA:產生彙編程式碼)
產生的彙編程式碼(test.asm):
    TITLE   test.cpp
    .386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT   SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT   ENDS
_DATA   SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA   ENDS
CONST   SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST   ENDS
_BSS   SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS   ENDS
_TLS   SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS   ENDS
FLAT   GROUP _DATA, CONST, _BSS
    ASSUME   CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC   [email protected]@3PAHA                   ; inbss
PUBLIC   [email protected]@3MA                   ; fA
PUBLIC   [email protected]@3PAHA                   ; indata
PUBLIC   [email protected]@3NA                   ; dbB
_BSS   SEGMENT
[email protected]@3PAHA DD 10H DUP (?)           ; inbss
[email protected]@3MA DD   01H DUP (?)               ; fA
_BSS   ENDS
_DATA   SEGMENT
[email protected]@3PAHA DD 01H                   ; indata
    DD   02H
    DD   03H
    DD   04H
    DD   05H
    DD   06H
    DD   07H
    DD   08H
    DD   09H
    ORG $+4007964
[email protected]@3NA DQ   04059000000000000r       ; 100   ; dbB
_DATA   ENDS
PUBLIC   _main
EXTRN   _printf:NEAR
_DATA   SEGMENT
$SG537   DB   '%d ', 00H
_DATA   ENDS
_TEXT   SEGMENT
_run$ = -400
_i$ = -404
_main   PROC NEAR
; File test.cpp
; Line 13
    push   ebp
    mov   ebp, esp
    sub   esp, 404               ; 00000194H
    push   edi
; Line 14
    mov   DWORD PTR _run$[ebp], 1
    mov   DWORD PTR _run$[ebp+4], 2
    mov   DWORD PTR _run$[ebp+8], 3
    mov   DWORD PTR _run$[ebp+12], 4
    mov   DWORD PTR _run$[ebp+16], 5
    mov   DWORD PTR _run$[ebp+20], 6
    mov   DWORD PTR _run$[ebp+24], 7
    mov   DWORD PTR _run$[ebp+28], 8
    mov   DWORD PTR _run$[ebp+32], 9
    mov   ecx, 91                   ; 0000005bH
    xor   eax, eax
    lea   edi, DWORD PTR _run$[ebp+36]
    rep stosd
; Line 15
    mov   DWORD PTR _i$[ebp], 0
    jmp   SHORT $L534
$L535:
    mov   eax, DWORD PTR _i$[ebp]
    add   eax, 1
    mov   DWORD PTR _i$[ebp], eax
$L534:
    cmp   DWORD PTR _i$[ebp], 1002000       ; 10H
    jge   SHORT $L536
; Line 16
    mov   ecx, DWORD PTR _i$[ebp]
    mov   edx, DWORD PTR [email protected]@3PAHA[ecx*4]
    push   edx
    push   OFFSET FLAT:$SG537
    call   _printf
    add   esp, 8
    jmp   SHORT $L535
$L536:
; Line 17
    xor   eax, eax
; Line 18
    pop   edi
    mov   esp, ebp
    pop   ebp
    ret   0
_main   ENDP
_TEXT   ENDS
END
----------------------------------------
通過彙編檔案可以看到,陣列inbss和indata位於不同的段(inbss位於bss段,而indata位於data段)
若把test.cpp中的indata陣列拿掉,檢視生成的exe檔案的大小,可以發現,indata拿掉之後exe檔案的大小小了很多。而若拿掉的是inbss陣列,exe檔案大小跟沒拿掉時相差無幾。

說明了:
bss段(未手動初始化的資料)並不給該段的資料分配空間,只是記錄資料所需空間的大小。
data(已手動初始化的資料)段則為資料分配空間,資料儲存在目標檔案中。

資料段包含經過初始化的全域性變數以及它們的值。BSS段的大小從可執行檔案中得到 ,然後連結器得到這個大小的記憶體塊,緊跟在資料段後面。當這個記憶體區進入程式的地址空間後全部清零。包含資料段和BSS段的整個區段此時通常稱為資料區。