聲明:本文為轉載的文章;並非由本人創作;發博文只是為了整理、記錄。 推薦的比較完全,比較清晰的文章(含圖):http://blog.csdn.net/sunny04/article/details/40627311 轉載時請註明出處和作者聯繫方式:http://blog.csdn.net/absur ...
聲明:本文為轉載的文章;並非由本人創作;發博文只是為了整理、記錄。
推薦的比較完全,比較清晰的文章(含圖):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意味著在數據段中存放變數)。除此以外,在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也會被存放回棧中。由於棧的先進後出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上講,我們可以把堆棧看成一個寄存、交換臨時數據的記憶體區。
棧的特點:
反覆使用,先進後出,臟記憶體,臨時性(函數不能返回棧變數的指針),棧會溢出。