.bss 節區(the .bss section)
/ bee-ess-ess /
假設你的程式宣告了一個一開始全為零的大陣列,例如一個全域範圍的一百萬位元組緩衝區。把一百萬個字面上的零位元組存進磁碟上的可執行檔會很蠢——這會無故膨脹檔案,因為每個位元組都一樣。.bss 節區是優雅的解法:它是程式存放零初始化與未初始化的全域與靜態變數之處,只記錄它們需要「多少」空間,不記錄內容。
具體來說,連結器為每個零初始化或未初始化的全域變數在 .bss 裡指定一個位置並算出總大小,但 .bss 節區在檔案本身裡基本上不佔位元組(它的檔案大小為零;只記錄它的記憶體大小)。載入時,載入器映射一塊那個大小的區域,而系統保證它一開始全為零。相對地,.data 放有非零初始值的全域變數,那些初始位元組真的存在檔案裡,因為它們必須被複製進來。所以「static int counter;」或「static char buf[1048576];」落在 .bss,而「static int counter = 7;」落在 .data。.bss 這個名字是來自一個舊組譯器的歷史縮寫(Block Started by Symbol);名字留下來了,雖然原意如今已無關緊要。
它在大小與速度兩方面都重要:有大型零初始化緩衝區的程式在磁碟上仍然很小、載入很快,因為把記憶體清零很便宜,且常由映射一個寫入時複製的零頁來處理。一個常見誤解是 .bss 變數在危險意義上是「未初始化」的——對全域與靜態變數,C 標準保證它們一開始為零,所以在賦值前讀取是有明確定義的。這個保證並不延伸到堆疊上的區域變數或 malloc() 來的記憶體,那些才是真正不確定的(calloc() 會清零,malloc() 不會)。
static char buffer[1 << 20]; // 1 MiB,全為零 -> 放在 .bss static int config = 42; // 非零初始值 -> 放在 .data // 在磁碟上:.bss 增加約 0 位元組;.data 存放代表 42 的那 4 個位元組。
零初始化的全域變數不花檔案空間(.bss 只記大小);非零初始值必須被存下來(.data)。
零的保證只適用於全域/靜態變數(標準會把它們零初始化)。區域變數與 malloc() 來的記憶體不會清零——在賦值前讀取它們是未定義或不確定的;需要零時請用 calloc()。