本文通過具體代碼示例,結合readelf與/proc工具,從實際內存地址角度完整演示C程序的四大分區分佈,並驗證各類變量的具體歸屬。
int ga1; // 未初始化全局變量
int ga2 = 1; // 已初始化全局變量
int func() {
int i = 0; // 局部非靜態變量
static int static_la1; // 未初始化局部靜態變量
static int static_la2 = 3; // 已初始化局部靜態變量
printf("Address of i: %p\n", &i);
printf("Address of static_la1: %p\n", &static_la1);
printf("Address of static_la2: %p\n", &static_la2);
while(i++ < 8) sleep(1);
return 0;
}
int main() {
int la1; // 局部非靜態變量
int la2 = 2; // 局部非靜態變量(已初始化)
int *p = malloc(3*sizeof(int)); // 動態分配內存
printf("Address of ga1: %p\n", &ga1);
printf("Address of ga2: %p\n", &ga2);
printf("Address of la1: %p\n", &la1);
printf("Address of la2: %p\n", &la2);
printf("Address of p: %p\n", p); // 堆內存地址
func();
free(p);
return 0;
}
Address of ga1: 0x804a040
Address of ga2: 0x804a030
Address of la1: 0xbfc7e7b4
Address of la2: 0xbfc7e7b8
Address of p: 0x8ffc008
func is running
Address of i: 0xbfc7e78c
Address of static_la1: 0x804a03c
Address of static_la2: 0x804a034
$ readelf -S program
...
[24] .data PROGBITS 0804a028 001028 000010 00 WA 0 0 4
[25] .bss NOBITS 0804a038 001038 00000c 00 WA 0 0 4
...
[13] .text PROGBITS 080483e0 0003e0 0002a2 00 AX 0 0 16
關鍵信息提取:
.text):0x080483e0 起,大小 0x2a2.data:0x0804a028 起,大小 0x10 (16字節).bss:0x0804a038 起,大小 0xc (12字節)| 變量 | 地址 | 分區判定邏輯 | 所屬分區 |
|---|---|---|---|
| ga2 | 0x804a030 | 在.data範圍內 (0x0804a028 ~ 0x0804a038) | .data |
| static_la2 | 0x804a034 | 在.data範圍內 | .data |
| ga1 | 0x804a040 | ≥ .bss起始 (0x0804a038) | .bss |
| static_la1 | 0x804a03c | ≥ .bss起始 | .bss |
| 變量 | 地址 | 分區判定 | 所屬分區 |
|---|---|---|---|
| la1 | 0xbfe01cf4 | 在棧區範圍內 | 棧區 |
| la2 | 0xbfe01cf8 | 在棧區範圍內 | 棧區 |
| p | 0x0964d008 | 在堆區範圍內 | 堆區 |
| i | 0xbfe01ccc | 在棧區範圍內 | 棧區 |

圖示直觀展示了從低地址到高地址的分區順序,以及堆棧相向增長的動態特性。
readelf看靜態段,/proc/maps看動態映射,printf("%p")看具體變量建議讀者在自己的環境編譯運行示例代碼,親自觀察地址分佈,建立具體的內存空間直覺。