目標(biāo)文件缨叫、可執(zhí)行程序及其他二進(jìn)制文件以ELF格式存儲在磁盤中,該文件有兩個重要的段(section)荔燎,即代碼段和數(shù)據(jù)段耻姥。
數(shù)據(jù)段又分為:.data 段 和 .bss段,其中.data段存儲已初始化的全局變量和靜態(tài)變量湖雹,.bss 段存儲未初始化的全局變量咏闪。
在C中,凡是在任何代碼塊之外聲明的變量總是存儲于靜態(tài)內(nèi)存中摔吏,也就是不屬于堆棧的內(nèi)存鸽嫂,這類變量稱為靜態(tài)(static)變量≌鹘玻——C和指針(p43)
這句話中有兩個概念需要搞清楚:
- 靜態(tài)內(nèi)存:指的是前文所述的數(shù)據(jù)段(.data 段 和 .bss段)据某,不包括堆棧。
-
靜態(tài)(static)變量:沒有static關(guān)鍵字的變量也可以是靜態(tài)變量诗箍,關(guān)鍵是存儲于靜態(tài)內(nèi)存中癣籽。實際上,當(dāng)static關(guān)鍵字用于不同上下文環(huán)境時,具有不同的意思筷狼。
同時瓶籽,我們還知道,靜態(tài)變量在程序運行之前被創(chuàng)建埂材,其分為兩種:已初始化的 和 未初始化的塑顺。- 已初始化的包括:static變量 (這里不能稱其為靜態(tài)變量)、 已初始化的全局變量俏险;static變量是默認(rèn)zero-initialization的严拒,所以就算未顯示初始化,也會被zero-initialization竖独; 全局變量 <=字面值(或常量)裤唠,即賦予字面值時會在編譯階段就被初始化,如:int g_var = 4莹痢;
- 未初始化的:即未初始化的全局變量种蘸。
如前文所述,前者存儲于.data段竞膳,后者存儲于.bss段劈彪。且前者的初始化是在編譯階段就完成的,后者的初始化時刻不一定顶猜,我們在后文中討論。但兩者的共同點是痘括,都是在運行前創(chuàng)建的长窄。
這時,我們也許想問纲菌,運行前是如何創(chuàng)建靜態(tài)變量的?
我們知道挠日,編譯-匯編后生成二進(jìn)制目標(biāo)文件(ELF格式),雖然還不是最終的可執(zhí)行文件翰舌,但已經(jīng)有了代碼段和數(shù)據(jù)段嚣潜,而此時數(shù)據(jù)段中的.data段中已經(jīng)有了‘已初始化的靜態(tài)變量’的磁盤存儲空間,并被填充了初始化值椅贱,在進(jìn)程加載時直接被映射至內(nèi)存空間中懂算。
而.bss段實際上未占據(jù)任何磁盤存儲空間,也就是徒有其名庇麦,無有其實计技,只是在ELF的section header table 中記錄其應(yīng)該分配到的磁盤存儲空間,而直到可執(zhí)行程序被加載到內(nèi)存中時山橄,加載器將依據(jù).bss段的section header中的信息垮媒,在內(nèi)存中為其分配空間。
總結(jié)來說就是,運行前創(chuàng)建分為兩種:編譯時創(chuàng)建——>加載時映射至內(nèi)存睡雇;加載時創(chuàng)建萌衬。
此時,我們?nèi)杂幸苫笏Вo態(tài)變量的創(chuàng)建肯定是在運行前了秕豫,那么未初始化變量的初始化到底是在什么時候進(jìn)行的?
有三個猜測:
- 鏈接時抗愁?前文中講馁蒂,直至進(jìn)程加載至內(nèi)存時才分配空間,自然無談初始化蜘腌。
- 加載進(jìn)程至內(nèi)存時:
(C和指針)在靜態(tài)變量的初始化中沫屡,我們可以把可執(zhí)行文件想要初始化的值放在當(dāng)程序執(zhí)行時變量將會使用的位置(注:實際上就是磁盤文件.data 段),當(dāng)可執(zhí)行文件載入到內(nèi)存時撮珠,這個已經(jīng)保存了正確初始值的位置將賦值給那個變量……如果不顯式地指定其初始值沮脖,靜態(tài)變量將初始化為0。
這段話說的比較晦澀芯急,因為它不想引入過多程序裝載過程的知識勺届。但我們也可以借此判斷,未初始化的全局變量實際上和static一樣會被初始化為0娶耍,只不過它是在可執(zhí)行文件載入到內(nèi)存時發(fā)生的免姿。
- 運行時,所謂運行時初始化榕酒,即動態(tài)初始化(dynamic-initialization)胚膊,然而C與C++不同,靜態(tài)變量不支持動態(tài)初始化想鹰。所以運行時也是不可能的紊婉。
//in C
int x = 5;
static y = x; //error
//in C++
int x = 5;
static y = x; //correct
最終總結(jié):
在C中,靜態(tài)變量辑舷,即全局變量和static變量喻犁,是在程序運行前創(chuàng)建的,其中已初始化的全局變量和static變量在編譯匯編成目標(biāo)文件時何缓,初始值就已經(jīng)保存在磁盤的.data段了肢础,進(jìn)程加載時將其映射到內(nèi)存空間即可;
未初始化的全局變量需要進(jìn)程加載時真正的為.bss段分配內(nèi)存空間碌廓,并賦值為0乔妈。靜態(tài)變量的創(chuàng)建和初始化都是在運行前完成的,切記C中不能動態(tài)初始化氓皱,這一點與C++不同路召。