.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(已手動初始化的資料)段則為資料分配空間,資料儲存在目標檔案中。