每個進程都在獨立的虛擬地址空間中運行。在 32 位系統中,4 GB 的地址空間被劃分為用戶空間(0 ~ 3 GB,即 0x00000000 ~ 0xBFFFFFFF)和內核空間(3 GB ~ 4 GB,即 0xC0000000 ~ 0xFFFFFFFF)。用戶空間進一步包含三個核心數據段:代碼段、數據段、堆棧段。
存放可執行文件的機器指令與程序定義的常量。為了防止運行時被意外修改,代碼段屬性為只讀。多個進程若執行同一程序,可共享同一代碼段副本,節省物理內存。
緊接代碼段之後,分為初始化數據段與未初始化數據段(BSS 段)。前者存放已賦初值的全局變量與靜態變量;後者存放未顯式初始化的全局變量,程序啟動時由內核自動清零。
malloc/new),釋放時歸還(free/delete)。堆向高地址增長,大小僅受虛擬地址空間限制。以下程序演示如何通過打印關鍵符號地址來觀察各段分佈:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int etext, edata, end; // 鏈接器定義的段邊界符號
char *g_pstr = "Global string\n"; // 初始化全局變量 → 數據段
char g_buffer[20]; // 未初始化全局變量 → BSS 段
inline void disp_addr(char *name, int addr) {
printf("Name: %-20s Address: %08x\n", name, addr);
}
void disp_var(char *p) {
char *buffer1;
disp_addr("buffer1 address:", (int)&buffer1); // 局部變量 → 棧
buffer1 = (char*)malloc(strlen(p) + 1); // 動態分配 → 堆
strcpy(buffer1, p);
printf("buffer1: %s\n", buffer1);
free(buffer1);
}
int main() {
int i = 0;
printf("Addr etext: %p\n", &etext); // 代碼段結束
printf("Addr edata: %p\n", &edata); // 初始化數據段結束
printf("Addr end: %p\n", &end); // BSS 段結束
disp_addr("function main()", (int)main);
disp_addr("function disp_var():", (int)disp_var);
disp_addr("g_pstr address:", (int)&g_pstr);
disp_addr("g_buffer address:", (int)&g_buffer);
disp_addr("i address:", (int)&i);
disp_var(g_pstr);
return 0;
}
Addr etext: 0x08049954
Addr edata: 0x08049958
Addr end: 0x08049950
Name: function main() Address: 0804850c
Name: function disp_var(): Address: 080484a7
Name: g_pstr address: Address: 08049930
Name: g_buffer address: Address: 0804993c
Name: i address: Address: bf8583ec
Name: buffer1 address: Address: bf8583bc
buffer1: Global string
etext、edata、end 依次標記代碼段、初始化數據段、BSS 段的結束邊界。main、disp_var)落在代碼段低地址區(0x08048xxx)。g_pstr、g_buffer 位於數據段/BSS 區(0x08049xxx)。i、buffer1 位於棧區高地址(0xbf8xxxxx),且 buffer1 的值指向堆區分配的內存。