五大内存分区
- 代码段(Text /.text 段)
● 存放:CPU 指令、函数汇编代码、跳转表
● 权限:读 R、执行 X,不可写 W
● 特点:程序加载时载入;尝试写入直接触发0xC0000005访问违例 - 数据段(.data)
● 存放:已经初始化的全局变量、static 静态变量
● 权限:可读、可写,不可执行
● 补充:字符串常量很多放在.rdata只读数据段(不属于.text,同样只读) - BSS 段(.bss)
● 存放:未初始化的全局变量、static 静态变量
● 特点:程序运行前操作系统统一清零;PE 文件里不占用磁盘空间,加载到内存才分配 - 堆(Heap)
● 存放:malloc / calloc / realloc动态申请内存
● 管理:手动申请、手动 free 释放,忘记释放造成内存泄漏
● 用途:动态数组、链表、自定义结构体 - 栈(Stack)
● 存放:局部变量、函数参数、返回地址、ebp 栈帧
● 管理:自动分配自动回收,函数退出直接释放栈帧
● 限制:栈容量很小,无限递归 / 大局部数组 → 栈溢出 Stack Overflow
例题解析
- 全局、局部变量地址分布
c
int g_num1 = 10; // .data段
int g_num2[100]; // .bss段
int main(void)
{
int a = 10; // 栈
int b; // 栈
char* str = "0xCC"; // "0xCC"字符串常量放在只读rdata段
return 0;
}
● str本身变量在栈,*str指向只读数据段
- 递归与栈溢出 每次递归调用都会新建栈帧,保存返回地址 + 局部变量
无终止条件 → 栈不断增长,耗尽栈空间 → Stack Overflow 崩溃
- CE 调试查看内存权限 进程加载后,每个内存页标记属性:R/W/X
● 代码段:R+X,禁止写,防代码注入
● 栈:默认无执行权限(DEP 保护)
● 堆:默认 R/W,无执行
内存权限实践代码
c
#include <stdio.h>
int g_data = 100; // .data
int g_bss[200]; // .bss
int main()
{
int stack_var = 20; // 栈
char* str = "hello";// str指针在栈;字符串常量在只读rdata
printf("栈变量地址:%p\n", &stack_var);
printf("data全局变量:%p\n", &g_data);
printf("bss全局数组:%p\n", &g_bss);
printf("字符串常量地址:%p\n", str);
return 0;
}
总结
- 只读字符串常量不在 data 段,在.rdata只读数据段,修改会崩溃
- static 静态变量,无论写在函数内部还是全局,都归属.data/.bss,不在栈
- 栈内存自动回收,但不清理旧数据,容易产生脏数据
- BSS 段在 PE 文件不占磁盘大小,加载到内存才分配并清零