一、簡述堆和棧的區別
維度 | 棧(Stack) | 堆(Heap) |
---|---|---|
生命周期 | 隨函數調用自動創建/銷毀 | 由程序員或垃圾回收器控制 |
分配速度 | 極快(僅移動指針) | 慢(需查找空閑塊、維護元數據) |
空間大小 | 較小(通常 MB 級) | 較大(受系統內存限制) |
連續性 | 連續內存,自上而下生長 | 非連續,易產生碎片 |
典型用途 | 局部變量、函數參數 | 動態數組、對象、大內存 |
線程安全 | 每個線程獨立棧 | 多線程共享,需同步 |
示例 | int a = 10; | int *p = new int(10); |
一句話:棧像自動售貨機,隨拿隨走;堆像倉庫,按需申請、手動歸還。
二、簡述C++的內存管理
C++ 的內存管理可簡化為 “三區域、兩手段、一原則”:
-
三區域
- 棧:函數幀自動創建/銷毀,速度最快。
- 堆:
new
/delete
或malloc
/free
手動申請/釋放,空間大。 - 靜態/全局區:編譯期確定,程序運行期間一直存在。
-
兩手段
- 手動管理:裸指針 +
new/delete
,高效但易漏/懸垂。 - RAII + 智能指針:
std::unique_ptr
、std::shared_ptr
/weak_ptr
把資源生命周期綁定到對象生命周期,自動釋放。
- 手動管理:裸指針 +
-
一原則
資源即對象(RAII):獲得資源即構造對象,離開作用域即釋放資源——讓編譯器替你做 delete。
一句話:
用智能指針和 RAII 包裝資源,告別手動 delete,把內存管理交給作用域。
三、malloc和局部變量分配在堆還是棧?
分配方式 | 所在區域 | 生命周期 | 示例 |
---|---|---|---|
malloc / new | 堆(Heap) | 程序員手動 free /delete 或程序結束 | int* p = (int*)malloc(4); |
局部變量 | 棧(Stack) | 離開作用域自動銷毀 | int x = 10; |
一句話:malloc
永遠落在堆;局部變量永遠落在棧。
四、程序有哪些section,分別的作用?程序啟動的過程?怎么判斷數據分配在棧上還是堆上?
(1)、典型可執行文件(ELF)里的 section(段)
(按低→高地址排列,括號內為常見段名)
Section | 作用 | 生命周期 |
---|---|---|
.text | 機器指令(代碼) | 只讀,整個進程 |
.rodata | 只讀常量(字符串、const 全局) | 同上 |
.data | 已初始化的全局/靜態變量 | 整個進程 |
.bss | 未初始化的全局/靜態變量(默認 0) | 整個進程 |
heap | 運行時動態分配(malloc/new ) | 程序員控制 |
共享庫映射區 | mmap 的 .so 文件、匿名映射 | 按需加載 |
stack | 函數幀、局部變量、返回地址 | 隨函數進出 |
命令行參數 & 環境變量 | argc, argv, envp | 進程啟動時由內核放進來 |
內核空間 | 內核代碼/數據(用戶不可見) | 整個系統 |
(2)、程序啟動的 9 步流程(Linux 為例)
- Shell
fork
→ 創建子進程 - 內核
execve
裝載 ELF - 讀 ELF header → 解析各個 section 偏移和長度
- 建立虛擬地址空間:
- 把
.text/.rodata/.data/.bss
映射進來 - 為 heap 預留一段匿名映射(
brk
起點) - 預留 stack 區域并設置 RSP
- 把
- 裝載動態鏈接器 (
ld-linux.so
) → 映射到共享區 - 重定位 & 符號解析 → 把
.so
的.text/.data
填進共享區 - 初始化 .bss 為 0
- 運行
.init
/__libc_start_main
→ 調全局對象構造函數 - 跳轉到
main
→ 用戶代碼開始
(3)、如何判斷數據在 棧 還是 堆?
場景 | 所在區域 | 判斷技巧 |
---|---|---|
int x = 5; | 棧 | 作用域結束即銷毀 |
int *p = new int(5); | 堆 | 離開作用域后 *p 仍有效,需要 delete |
static int x; | 全局區(.data/.bss) | 整個進程生命周期 |
`const char *s = “hello”; | ` 只讀段(.rodata) | 地址位于低地址只讀區域 |
malloc(…) 返回值 | 堆 | gdb / pmap / cat /proc/$$/maps 可見匿名映射 |
最實用的運行時判斷
pmap -x <pid>
- 區間名
[stack]
→ 棧 - 區間名
[heap]
→ 堆 - 區間名
/lib/ld-linux.so
→ 共享區
一句話總結
代碼在 .text,常量在 .rodata,全局/靜態在 .data/.bss,動態在 heap,局部在 stack;啟動時內核按 ELF 把段映射進虛擬地址空間,運行時看作用域和分配 API 即可區分棧與堆。
五、初始化為0的全局變量在bss還是data
初始化為 0(或全零)的全局變量 放在 .bss
,而不是 .data
。
(.data
只收“非零初始值”的全局/靜態變量。)
int g1 = 0; // .bss
int g2 = 42; // .data
static int s1 = 0; // .bss
static int s2 = 1; // .data
一句話:
零初始化的全局/靜態變量進
.bss
,非零才進.data
。
六、簡述C++中內存對齊的使用場景
一句話:
“讓數據在內存里按照 CPU 最喜歡的地址倍數排排坐,從而少一次訪存、多一次 SIMD。”
1?? 典型場景
場景 | 原因 | 關鍵詞 |
---|---|---|
結構體/類成員布局 | 避免空洞、保證原子變量對齊 | #pragma pack , alignas |
SSE/AVX 指令 | 128/256/512 bit 必須 16/32/64 字節對齊 | alignas(16) |
malloc/new 無法滿足 | 自定義對齊內存 | std::aligned_alloc , std::aligned_storage |
共享內存/網絡協議 | 跨平臺二進制兼容 | alignas(uint32_t) |
原子操作 | std::atomic<T> 要求 T 自然對齊 | alignof(std::max_align_t) |
2?? 一行代碼示例
struct alignas(32) Vec4 {float x, y, z, w;
}; // 起始地址一定是 32 的倍數,可直接 `_mm256_load_ps`
3?? 口訣
“原子對齊保并發,SIMD 對齊保速度,協議對齊保兼容。”