資料段data、bss、rodata

流水江湖發表於2024-05-08

.bss: 未被初始化的全域性的C變數。這一節在o檔案中不佔實際的空間,只是一個place holder。o檔案格式之所以區分初始化的變數和未被初始化的變數是因為處於空間利用率上的考慮。沒有被初始化的變數確實沒有必要佔用實際的磁碟空間。

https://www.cnblogs.com/idorax/p/6400210.html

1.1 data 段


用來儲存已經初始化的全域性變數,也屬於靜態記憶體分配區,會佔用程式檔案空間。

與bss相比,data就容易明白多了,它的名字就暗示著裡面存放著資料。當然,如果資料全是零,為了最佳化考慮,編譯器把它當作bss處理。通俗的說,data指那些初始化過(非零)的非const的全域性變數。它有什麼特點呢,我們還是來看看一個小程式的表現。
int data_array[1024 * 1024] = {1};

int main(int argc, char* argv[])
{
return 0;
}

[root@localhost data]# gcc -g data.c -o data.exe
[root@localhost data]# ll
total 4112
-rw-r--r-- 1 root root 85 Jun 22 14:35 data.c
-rwxr-xr-x 1 root root 4200025 Jun 22 14:35 data.exe

僅僅是把初始化的值改為非零了,檔案就變為4M多。由此可見,data型別的全域性變數是即佔檔案空間,又佔用執行時記憶體空間的。

1.2 bss 段

BSS(Block Started by Symbol)用來儲存未初始化的全域性變數和靜態變數,全域性變數或靜態變數值為0或NULL(對於指標變數而言)的通常會被編譯器認為未初始化,屬於靜態記憶體分配區,不會佔用程式檔案空間,不儲存這些變數在外存上,但是還是會佔用一部分空間,這些空間用來標識未初始化的變數大小、屬性等資訊。

BSS段(bsssegment)通常是指用來存放程式中未初始化的全域性變數的一塊記憶體區域

並不給該段的資料分配空間,只是記錄資料所需要的空間大小。

它有什麼特點呢,讓我們來看看一個小程式的表現。
int bss_array[1024 * 1024] = {0};

int main(int argc, char* argv[])
{
return 0;
}
[root@localhost bss]# gcc -g bss.c -o bss.exe
[root@localhost bss]# ll
total 12
-rw-r--r-- 1 root root 84 Jun 22 14:32 bss.c
-rwxr-xr-x 1 root root 5683 Jun 22 14:32 bss.exe

變數bss_array的大小為4M,而可執行檔案的大小隻有5K。 由此可見,bss型別的全域性變數只佔執行時的記憶體空間,而不佔檔案空間。

另外,大多數作業系統,在載入程式時,會把所有的bss全域性變數全部清零,無需要你手工去清零。但為保證程式的可移植性,手工把這些變數初始化為0也是一個好習慣。

1.3 rodata
rodata的意義同樣明顯,ro代表read only,即只讀資料(const)。

關於rodata型別的資料,要注意以下幾點:

常量不一定就放在rodata裡,有的立即數直接編碼在指令裡,存放在程式碼段(.text)中。
對於字串常量,編譯器會自動去掉重複的字串,保證一個字串在一個可執行檔案(EXE/SO)中只存在一份複製。
rodata是在多個程序間是共享的,這可以提高空間利用率。
在有的嵌入式系統中,rodata放在ROM(如norflash)裡,執行時直接讀取ROM記憶體,無需要載入到RAM記憶體中。
在嵌入式linux系統中,透過一種叫作XIP(就地執行)的技術,也可以直接讀取,而無需要載入到RAM記憶體中。
由此可見,把在執行過程中不會改變的資料設為rodata型別的,是有很多好處的:在多個程序間共享,可以大大提高空間利用率,甚至不佔用RAM空間。同時由於rodata在只讀的記憶體頁面(page)中,是受保護的,任何試圖對它的修改都會被及時發現,這可以幫助提高程式的穩定性。

text段:

程式碼段(codesegment/textsegment)通常是指用來存放程式執行程式碼的一塊記憶體區域。這部分割槽域的大小在程式執行前就已經確定,並且記憶體區域通常屬於只讀,某些架構也允許程式碼段為可寫,即允許修改程式。在程式碼段中,也有可能包含一些只讀的常數變數,例如字串常量等。

heap堆:

堆是用於存放程序執行中被動態分配的記憶體段,它的大小並不固定,可動態擴張或縮減。當程序呼叫malloc等函式分配記憶體時,新分配的記憶體就被動態新增到堆上(堆被擴張);當利用free等函式釋放記憶體時,被釋放的記憶體從堆中被剔除(堆被縮減)

stack棧:

是使用者存放程式臨時建立的區域性變數,也就是說我們函式括弧“{}”中定義的變數(但不包括static宣告的變數,static意味著在資料段中存放變數)。除此以外,在函式被呼叫時,其引數也會被壓入發起呼叫的程序棧中,並且待到呼叫結束後,函式的返回值也會被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來儲存/恢復呼叫現場。從這個意義上講,我們可以把堆疊看成一個寄存、交換臨時資料的記憶體區。

常量段:

常量段一般包含編譯器產生的資料(與只讀段包含使用者定義的只讀資料不同)。比如說由一個語句a=2+3編譯器把2+3編譯期就算出5,存成常量5在常量段中

1.8.變數與關鍵字


static關鍵字

用途太多,以致於讓新手模糊。不過,總結起來就有兩種作用,改變生命期和限制作用域。如:
l 修飾inline函式:限制作用域
l 修飾普通函式:限制作用域
l 修飾區域性變數:改變生命期
l 修飾全域性變數:限制作用域

靜態區域性變數屬於靜態儲存類別,在靜態儲存區內分配儲存單元。在整個程式執行期間都不釋放

const 關鍵字

倒是比較明瞭,用const修飾的變數放在rodata裡,字串預設就是常量。對const,注意以下幾點就行了。
l 指標常量:指向的資料是常量。如 const char* p = “abc”; p指向的內容是常量 ,但p本身不是常量,你可以讓p再指向”123”。
l 常量指標:指標本身是常量。如:char* const p = “abc”; p本身就是常量,你不能讓p再指向”123”。
l 指標常量 + 常量指標:指標和指標指向的資料都是常量。const char* const p =”abc”; 兩者都是常量,不能再修改。

volatile關鍵字

volatile
[ˈvɒləˌtaɪl]

通常用來修飾多執行緒共享的全域性變數和IO記憶體。告訴編譯器,不要把此類變數最佳化到暫存器中,每次都要老老實實的從記憶體中讀取,因為它們隨時都可能變化。這個關鍵字可能比較生僻,但千萬不要忘了它,否則一個錯誤讓你除錯好幾天也得不到一點線索。

問題1,二進位制檔案不包含BSS段,那把BSS段放在哪
答:修改有1000個全域性變數,難道要BIN裡要存1000個0嗎?在連結指令碼里把BSS段組織在一起,記下它的起始地址、結束地址,重定位後把這塊記憶體清0即可

問題2:全域性變數不初始化的話預設初始化為零,幹嘛還要手動清零
答:因為它是在BSS段的
————————————————

版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

原文連結:https://blog.csdn.net/y13182588139/article/details/125559846

相關文章