C/C++ —— 程序內存的分區
本文轉載自:https://blog.csdn.net/shulianghan/article/details/20472269
C語言程序內存分配
(1) 內存分區狀況
棧區 (stack) :
-- 分配, 釋放方式 : 由編譯器自動分配 和 釋放;
-- 存放內容 : 局部變量, 參數;
-- 特點 : 具有 後進先出 特性, 適合用於 保存 回復 現場;
堆區 (heap) :
-- 分配, 釋放方式 : 由程序員手動 分配(malloc) 和 釋放(free), 如果程序員沒有釋放, 那麽程序退出的時候, 會自動釋放;
-- 存放內容 : 存放程序運行中 動態分配 內存的數據;
-- 特點 : 大小不固定, 可能會動態的 放大 或 縮小;
堆區內存申請 :
-- 申請過程 : OS中有一個記錄空閑內存地址的鏈表, 如果程序員申請內存, 就會找到空間大於申請內存大小的節點, 將該節點從空間內存鏈表中刪除, 並分配該節點;
-- 剩余內存處理 : 系統會將多余的部分重新放回 空閑內存鏈表中;
-- 首地址記錄大小 : 分配內存的首地址存放該堆的大小, 這樣釋放內存的時候才能正確執行;
全局區/靜態區 (數據段 data segment / bss segment) :
-- 分配, 釋放方式 : 編譯器分配內存, 程序退出時系統自動釋放內存;
-- 存放內容 : 全局變量, 靜態變量;
-- 特點 : 全局變量 和 靜態變量存儲在一個區域, 初始化的兩種變量 和 未初始化的 存儲在不同區域, 但是兩個區域是相鄰的;
常量區 :
-- 分配, 釋放方式 : 退出程序由系統自動釋放;
-- 存放內容 : 常量; (比如char *s = "hello",此處的hello就存儲在常量區)
代碼區 (text segment) :
-- 分配, 釋放方式 : 編譯器分配內存, 程序退出時系統自動釋放內存;
-- 存放內容 : 存放函數體的二進制代碼。
內存存放順序 (由上到下) : 棧區 -> 堆區 -> 全局區 -> 常量區 -> 代碼區;
(2) 內存分配方式
全局內存分配 :
-- 生命周期 : 編譯時分配內存, 程序退出後釋放內存, 與 程序 的生命周期相同;
-- 存儲內容 : 全局變量, 靜態變量;
棧內存分配 :
-- 生命周期 : 函數執行時分配內存, 執行結束後釋放內存;
-- 特點 : 該分配運算由處理器處理, 效率高, 但是棧內存控件有限;
堆內存分配 :
-- 生命周期 : 調用 malloc()開始分配, 調用 free()釋放內存, 完全由程序員控制;
-- 謹慎使用 : 如果分配了 沒有釋放, 會造成內存泄露, 如果頻繁 分配 釋放 會出現內存碎片;
(3) register變量
使用場景 : 如果 一個變量使用頻率特別高, 可以將這個變量放在 CPU 的寄存器中;
-- 修飾限制 : 只有 局部變量 和 參數 可以被聲明為 register變量, 全局 和 靜態的不可以;
-- 數量限制 : CPU 寄存器 很寶貴, 不能定義太多register變量;
(4) extern 變量
extern變量概念 : 聲明外部變量, 外部變量就是在函數的外部定義的變量, 在本函數中使用;
-- 作用域 : 從外部變量定義的位置開始, 知道本源碼結束都可以使用, 但是只能在定義extern後面使用, 前面的代碼不能使用;
-- 存放位置 : 外部變量 存放在 全局區;
extern變量作用 : 使用extern修飾外部變量, ① 擴展外部變量在本文件中的作用域, ② 將外部變量作用域從一個文件中擴展到工程中的其它文件;
extern聲明外部變量的情況 :
-- 單個文件內聲明 : 如果不定義在文件開頭, 其作用範圍只能是 定義位置開始, 文件結束位置結束;
-- 多個文件中聲明 : 兩個文件中用到一個外部變量, 只能定義一次, 編譯 和 連接的時候, 如果沒有這個外部變量, 系統會知道這個外部變量在別處定義, 將另一個文件中的外部變量擴展到本文件中;
extern編譯原則 :
-- 本文件中能找到 : 編譯器遇到 extern 的時候, 現在本文件中找外部變量的定義的位置, 如果找到, 就將作用域擴展到 定義的位置 知道文件結束;
-- 本文件中找不到 : 如果本文件中找不到, 連接其它文件找外部變量定義, 如果找到, 將外部變量作用域擴展到本文件中;
-- 外部文件找不到 : 報錯;
使用效果 : extern 使用的時候, 可以不帶數據類型;
-- 本文件 : int A = 0; 在第10行, extern A 在第一行, 那麽A的作用域就擴展為從第一行到文件末尾;
-- 多文件 : 在任意文件中定義了 int A = 0; 在本文件中聲明 extern A, 那麽從當前位置到文件末尾都可以使用該變量;
(5) static變量 與 全局變量區別
static 變量 與 全局變量 相同點 : 全局變量是靜態存儲的, 存儲的方式 和 位置基本相同;
static 變量 與 全局變量不用點 : 全局變量的作用域是 整個項目工程 橫跨過個文件, 靜態變量的作用域是 當前文件, 其它文件中使用是無效的;
變量存儲位置 : 全局變量 和 靜態變量 存放在 全局區/靜態去, 局部變量存放在 棧區(普通變量) 和 堆區(指針變量);
變量靜態化 :
-- 局部變量 : 局部變量 加上 static , 相當於將局部變量的生命周期擴大到了整個文件, 作用域不改變;
-- 全局變量 : 全局變量 加上 static , 相當於將全局變量的作用域縮小到了單個文件, 生命周期是整個程序的周期;
關於函數頭文件的引申 :
-- 內部函數 : 單個文件中使用的內部函數, 僅在那個特定文件中定義函數即可;
-- 全局函數 : 如果要在整個工程中使用一個全局函數, 需要將這個函數定義在一個頭文件中;
static變量與普通變量區別 :
-- static全局變量 與 全局變量區別 : static 全局變量 只初始化一次, 防止在其它文件中使用;
-- static局部變量 與 局部變量區別 : static 局部變量 只初始化一次, 下一次依據上一次結果;
static函數與普通函數區別 : static 函數在內存中只保留一份, 普通函數 每調用一次, 就創建一個副本;
.
(6) 堆 和 棧比較
堆(heap)和棧(stack)區別 :
-- 申請方式 : stack 由系統自動分配, heap 由程序員進行分配;
-- 申請響應 : 如果 stack 沒有足夠的剩余空間, 就會溢出; 堆內存從鏈表中找空閑內存;
-- 內存限制 : stack 內存是連續的, 從高位向低位擴展, 而且很小, 只有幾M, 是事先定好的, 在文件中配置; heap 是不連續的, 從低位向高位擴展, 系統是由鏈表控制空閑程序, 鏈表從低地址到高地址, 堆大小受虛擬內存限制, 一般32位機器有4G heap;
-- 申請效率 : stack 由系統分配, 效率高; heap 由程序員分配, 速度慢, 容易產生碎片;
(7) 各區分布情況
由上到下順序 : 棧區(stack) -> 堆區(heap) -> 全局區 -> 字符常量區 -> 代碼區;
驗證分區狀況 :
-- 示例程序 :
1 /************************************************************************* 2 > File Name: memory.c 3 > Author: octopus 4 > Mail: octopus_work.163.com 5 > Created Time: Mon 10 Mar 2014 08:34:12 PM CST 6 ************************************************************************/ 7 8 #include<stdio.h> 9 #include<stdlib.h> 10 11 int global1 = 0, global2 = 0, global3 = 0; 12 13 void function(void) 14 { 15 int local4 = 0, local5 = 0, local6 = 0; 16 static int static4 = 0, static5 = 0, static6 = 0; 17 int *p2 = (int*)malloc(sizeof(int)); 18 19 printf("子函數 局部變量 : \n"); 20 printf("local4 : %p \n", &local4); 21 printf("local5 : %p \n", &local5); 22 printf("local6 : %p \n", &local6); 23 24 printf("子函數 指針變量 : \n"); 25 printf("p2 : %p \n", p2); 26 27 printf("全局變量 : \n"); 28 printf("global1 : %p \n", &global1); 29 printf("global2 : %p \n", &global2); 30 printf("global3 : %p \n", &global3); 31 32 printf("子函數 靜態變量 : \n"); 33 printf("static4 : %p \n", &static4); 34 printf("static5 : %p \n", &static5); 35 printf("static6 : %p \n", &static6); 36 37 printf("子函數地址 : \n"); 38 printf("function : %p \n", function); 39 } 40 41 int main(int argc, char **argv) 42 { 43 int local1 = 0, local2 = 0, local3 = 0; 44 static int static1 = 0, static2 = 0, static3 = 0; 45 int *p1 = (int*)malloc(sizeof(int)); 46 const int const1 = 0; 47 char *char_p = "char"; 48 49 printf("主函數 局部變量 : \n"); 50 printf("local1 : %p \n", &local1); 51 printf("local2 : %p \n", &local2); 52 printf("local3 : %p \n", &local3); 53 printf("const1 : %p \n", &const1); 54 55 printf("主函數 指針變量 : \n"); 56 printf("p1 : %p \n", p1); 57 58 printf("全局變量 : \n"); 59 printf("global1 : %p \n", &global1); 60 printf("global2 : %p \n", &global2); 61 printf("global3 : %p \n", &global3); 62 63 printf("主函數 靜態變量 : \n"); 64 printf("static1 : %p \n", &static1); 65 printf("static2 : %p \n", &static2); 66 printf("static3 : %p \n", &static3); 67 68 printf("字符串常量 : \n"); 69 printf("char_p : %p \n", char_p); 70 71 printf("主函數地址 : \n"); 72 printf("main : %p \n", main); 73 74 75 printf("= = = = = = = = = = = = = = = \n"); 76 77 function(); 78 79 return 0; 80 }
-- 執行結果 :
1 [root@ip28 pointer]# gcc memory.c 2 [root@ip28 pointer]# ./a.out 3 主函數 局部變量 : 4 local1 : 0x7fff75f5eedc 5 local2 : 0x7fff75f5eed8 6 local3 : 0x7fff75f5eed4 7 const1 : 0x7fff75f5eed0 8 主函數 指針變量 : 9 p1 : 0x19bad010 10 全局變量 : 11 global1 : 0x600e14 12 global2 : 0x600e18 13 global3 : 0x600e1c 14 主函數 靜態變量 : 15 static1 : 0x600e34 16 static2 : 0x600e30 17 static3 : 0x600e2c 18 字符串常量 : 19 char_p : 0x4009f7 20 主函數地址 : 21 main : 0x40065f 22 = = = = = = = = = = = = = = = 23 子函數 局部變量 : 24 local4 : 0x7fff75f5eea4 25 local5 : 0x7fff75f5eea0 26 local6 : 0x7fff75f5ee9c 27 子函數 指針變量 : 28 p2 : 0x19bad030 29 全局變量 : 30 global1 : 0x600e14 31 global2 : 0x600e18 32 global3 : 0x600e1c 33 子函數 靜態變量 : 34 static4 : 0x600e28 35 static5 : 0x600e24 36 static6 : 0x600e20 37 子函數地址 : 38 function : 0x400528
C/C++ —— 程序內存的分區