理解 C/C++ 程式在虛擬記憶體中的分佈,是掌握**指標運算、動態記憶體管理、緩衝區溢位、栈溢位、效能優化、逆向工程、嵌入式開發**的基石。本文系統梳理經典的 **Linux x86/x64 進程虛擬地址空間佈局**(高地址 → 低地址)。
高地址 (0xFFFF...FFFF)
+------------------------+
| 內核空間 (1GB/2GB) | 用戶程式不可直接訪問,系統呼叫陷入
+------------------------+
| 用戶空間 |
| +------------------+ |
| | 棧 Stack | | ↓ 向下增長 (高→低)
| | (區域變數、 | |
| | 呼叫框架、 | |
| | 返回地址) | |
| +------------------+ |
| | ↓ | | 未映射區域 (Guard Page)
| | ↑ | |
| | 堆 Heap | | ↑ 向上增長 (低→高)
| | (malloc/new、 | |
| | 動態記憶體) | |
| +------------------+ |
| | BSS 段 | | 未初始化全域/靜態變數 (編譯器置 0)
| | (.bss) | |
| +------------------+ |
| | 數據段 Data | | 已初始化全域/靜態變數
| | (.data) | |
| +------------------+ |
| | 程式碼段 Text | | 唯讀:指令、常數、字面值
| | (.text/.rodata) | |
+------------------------+
低地址 (0x0000...0000)
.text / .rodataconst 修飾的全域變數 (編譯器優化後)、函數本體。SIGSEGV (Segmentation Fault)。.datastatic 區域變數、static 類成員變數。int g_val = 42; static int s_val = 10;.bss (Block Started by Symbol)int g_arr[1000]; static int counter; int g_zero = 0;int big[1000000] = {0}; 在 `.bss` 只佔幾 byte 元資料,在 `.data` 會佔 4MB 檔案體積。malloc/free、new/delete、brk/mmap 系統呼叫動態管理。sbrk/mmap 向核心申請)。int a[1000000]、無限遞迴。struct Foo {
char c; // offset 0, size 1
// 3 bytes padding (alignment 4)
int i; // offset 4, size 4
// 4 bytes padding (alignment 8 for double)
double d; // offset 16, size 8
}; // sizeof(Foo) = 24, alignof(Foo) = 8
位於**棧頂附近** (高地址端),棧向下增長時會「壓過」它們:
argc, argv[], envp[] → 字串實際內容 → 輔助向量 (AT_*)
#include <stdio.h>
#include <stdlib.h>
int g_data = 42; // .data
int g_bss; // .bss
const char *g_ro = "ro"; // .rodata (指標在 .data, 字串在 .rodata)
void print_segments() {
int local = 99; // stack
int *heap = malloc(4); // heap
printf("Code (.text) : %p (main)\n", (void*)main);
printf("RO Data (.rodata): %p\n", (void*)g_ro);
printf("Data (.data) : %p\n", (void*)&g_data);
printf("BSS (.bss) : %p\n", (void*)&g_bss);
printf("Heap : %p\n", (void*)heap);
printf("Stack : %p\n", (void*)&local);
free(heap);
}
int main() {
print_segments();
return 0;
}
典型輸出 (Linux x64 ASLR 開啟):
Code (.text) : 0x55a3b1c3d1a9
RO Data (.rodata): 0x55a3b1c3e004
Data (.data) : 0x55a3b1e41014
BSS (.bss) : 0x55a3b1e41040
Heap : 0x55a3b3c5e2a0
Stack : 0x7ffc8b3a9b3c
觀察:Code/Data/BSS 相鄰 (同一映射區域),Heap 在中間偏上,Stack 在用戶空間頂端附近。**ASLR (Address Space Layout Randomization)** 使每次運行基址不同,增強安全性。
| 特性 | Linux/Windows (OS) | 裸機/RTOS (無 MMU) |
|---|---|---|
| 虛擬記憶體 | 有 (MMU) | 無 (物理地址 = 虛擬地址) |
| 堆 | 動態向核心申請 | 靜態記憶體池 (linker script 定義) |
| 棧 | 每執行緒一個, 動態增長 | 固定位置/大小 (linker script 定義) |
| .bss 初始化 | 核心載入時清零 | 啟動代碼 (crt0) 手動清零 |
| 保護機制 | 頁表權限 (R/W/X) | MPU (Memory Protection Unit) 可選 |
總結: C/C++ 記憶體佈局不是抽象理論,而是直接決定了**指標有效性、物件生命週期、效能特徵、安全邊界**。掌握「五大段」分佈、增長方向、管理方式、對齊規則,能讓你在面對 Segment Fault、記憶體洩漏、緩衝區溢位、效能瓶頸時,直擊本質、精準定位、從容解決。建議配合 `gdb`、`pmap`、`/proc/pid/maps`、`objdump -h` 等工具實地觀察驗證。