數據段(BSS段、DATA段)、代碼段(.RODATA)、堆棧段的區別


聲明:本文為轉載的文章;並非由本人創作;發博文只是為了整理、記錄。

推薦的比較完全,比較清晰的文章(含圖):http://blog.csdn.net/sunny04/article/details/40627311

 

轉載時請注明出處和作者聯系方式:http://blog.csdn.net/absurd

作者聯系方式:李先靜 <xianjimli at hotmail dot com>

更新時間:2007-7-9

 

    全局變量是放在全局內存中的,但反過來卻未必成立。用static修飾的局部變量就是放在放全局內存的,它的作用域是局部的,但生命期是全局的。在有的嵌入式平台中,堆實際上就是一個全局變量,它占用相當大的一塊內存,在運行時,把這塊內存進行二次分配。這里我們並不強調全局變量和全局內存的差別。在本文中,全局強調的是它的生命期,而不是它的作用域,所以有時可能把兩者的概念互換。
 
    一般來說,在一起定義的兩個全局變量,在內存的中位置是相鄰的。這是一個簡單的常識,但有時挺有用,如果一個全局變量被破壞了,不防先查查其前后相關變量的訪問代碼,看看是否存在越界訪問的可能。
 
    在ELF(Executable and Linkable Format)格式的可執行文件中,全局內存包括三種:bss、data和rodata。其它可執行文件格式與之類似。了解了這三種數據的特點,我們才能充分發揮它們的長處,達到速度與空間的最優化。
 
數據段是包含了bss段和data段。
 
1.bss (Block Started by Symbol)段(bss segment)
        又叫ZI(zero inital)段,通俗的說,bss是指那些沒有初始化的和初始化為0的全局變量
        由原文的例子可見:bss類型的全局變量只占運行時的內存空間,而不占文件空間。
注:多數操作系統,在加載程序時,會把所有的bss全局變量全部清零,無需要你手工去清零。但為保證程序的可移植性,手工把這些變量初始化為0也是一個好習慣。
 
2.data(data segment)段      通俗的說,data指那些初始化過(非零)的非const的全局變量。如果數據全是零,為了優化考慮,編譯器把它當作bss處理。所以bss屬於一個特殊的data。
      由原文的例子可見:data類型的全局變量是即占文件空間,又占用運行時內存空間的。
 
3.rodata
      rodata的意義同樣明顯,ro代表read only,即只讀數據(const)。關於rodata類型的數據,要注意以下幾點:
         1.常量不一定就放在rodata里,有的立即數直接編碼在指令里,存放在代碼段(.text)中。
         2.對於字符串常量,編譯器會自動去掉重復的字符串,保證一個字符串在一個可執行文件(EXE/SO)中只存在一份拷貝。
         3.rodata是在多個進程間是共享的,這可以提高空間利用率。
         4.在有的嵌入式系統中,rodata放在ROM(如norflash)里,運行時直接讀取ROM內存,無需要加載到RAM內存中。
         5.在嵌入式linux系統中,通過一種叫作XIP(就地執行)的技術,也可以直接讀取,而無需要加載到RAM內存中。
      由此可見,把在運行過程中不會改變的數據設為rodata類型的,是有很多好處的:在多個進程間共享,可以大大提高空間利用率,甚至不占用RAM空間。同時由於rodata在只讀的內存頁面(page)中,是受保護的,任何試圖對它的修改都會被及時發現,這可以幫助提高程序的穩定性。
 

4.變量與關鍵字
static關鍵字用途太多,以致於讓新手模糊。不過,總結起來就有兩種作用,改變生命期和限制作用域。如:

         1.修飾inline函數:限制作用域
         2.修飾普通函數:限制作用域
         3.修飾局部變量:改變生命期
         4.修飾全局變量:限制作用域

const 關鍵字倒是比較明了,用const修飾的變量放在rodata里,字符串默認就是常量。對const,注意以下幾點就行了。
         1.指針常量:指向的數據是常量。如 const char* p = “abc”; p指向的內容是常量 ,但p本身不是常量,你可以讓p再指向”123”。(注:其中的“abc”是放在代碼段(.text)的,這個的格式也可以寫為   char const *p="abc";  )
         2.常量指針:指針本身是常量。如:char* const p = “abc”; p本身就是常量,你不能讓p再指向”123”。
         3.指針常量 + 常量指針:指針和指針指向的數據都是常量。const char* const p =”abc”; 兩者都是常量,不能再修改。

violatile關鍵字通常用來修飾多線程共享的全局變量和IO內存。告訴編譯器,不要把此類變量優化到寄存器中,每次都要老老實實的從內存中讀取,因為它們隨時都可能變化。這個關鍵字可能比較生僻,但千萬不要忘了它,否則一個錯誤讓你調試好幾天也得不到一點線索。

(注:這個相當於stm32文件中定義的__IO,代表了每次讀取用這個關鍵詞定義的值的時候,需要去內存中實時的讀取。也就是說每次得到的都是最新的值)

以下為補充:

轉載於:

http://blog.csdn.net/jxhui23/article/details/8064766

5.代碼段:

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

6.堆(heap):

      堆管理器是操作系統的一個模塊。是用於存放進程運行中被動態分配的內存段,它的大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)。

      堆的特點:

    需要手動申請(malloc)和釋放(free)

    臟內存,

    臨時性(在malloc和free之間能訪問)。

  (注:在測試的時候,堆free還能訪問其內部的內存,但是內部的內存值可能不正確了。)

    memset(p,0,10);//memset是對p當前位置后面的10個字節賦予0的初值。通常用於清0。
    malloc返回的是一個void類型的指針。返回的值表示一個內存地址。失敗返回NULL。
    使用的過程:申請(malloc)->檢驗是否為空內存->使用申請的內存->釋放申請的內存(free(p))。

    malloc位於stdlib這個頭文件中。

    "程序泄露"又叫"吃內存"

    gcc(linux)中int malloc(0)其實不是沒有分配內存,它分配了0x10個內存;gcc默認返回最小16字節的內存塊。

7.棧(stack)

      棧又稱堆棧, 是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味着在數據段中存放變量)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束后,函數的返回值也會被存放回棧中。由於棧的先進后出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的內存區。

棧的特點:

     反復使用,先進后出,臟內存,臨時性(函數不能返回棧變量的指針),棧會溢出。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM