轉自 FoxWolf的Blog :http://blogger.org.cn/blog/more.asp?name=FoxWolf&id=29997
【例一】
用
cl
編譯兩個小程序如下:
程序
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
卻需要佔用,其內容由程序初始化,因此造成了上述情況。
【例二】
編譯如下程序(
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 ?inbss@@3PAHA
; inbss
PUBLIC ?fA@@3MA ; fA
PUBLIC
?indata@@3PAHA ; indata
PUBLIC ?dbB@@3NA
; dbB
_BSS SEGMENT
?
inbss
@@3PAHA
DD 0f4a10H DUP (?)
; inbss
?fA@@3MA DD 01H DUP (?) ; fA
_BSS ENDS
_DATA SEGMENT
?
indata
@@3PAHA
DD 01H ; indata
DD 02H
DD 03H
DD 04H
DD 05H
DD 06H
DD 07H
DD 08H
DD 09H
ORG $+4007964
?dbB@@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 ; 000f4a10H
jge SHORT $L536
;
Line 16
mov ecx, DWORD PTR _i$[ebp]
mov edx, DWORD
PTR ?inbss@@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 段的整個區段此時通常稱爲數據區。