程序内存分布
C语言中的程序存储区
程序中的栈
- 栈是现代计算机程序里最为重要的概念之一
- 栈在程序中用于维护函数调用上下文
- 函数中的参数和局部变量存储在栈上
- 栈保存了一个函数调用所需的维护信息
- 参数
- 返回地址
- 局部变量
- 调用上下文
- ····
函数调用过程
- 每次函数调用都对应着一个栈上的活动
- 函数调用栈上的数据
- 函数调用时,对应的栈空间在函数返回前是专用的
- 函数调用结束后,栈空间被释放,数据不再有效
程序中的堆
- 堆是程序中一块预留的内存空间,可由程序自由使用
- 堆中被程序申请使用的内存在被主动释放前将一直有效
为什么有了栈还需要堆?
栈上的数据在函数返回后就会被释放掉,无法传递到函数外
- 系统对堆空间的管理方式
- 空闲链表法、位图法、对象池法等
程序中的静态存储区
- 静态存储区随着程序的运行而分配空间
- 静态存储区的生命周期直到程序运行结束
- 在程序的编译期静态存储区的大小就已经确定
- 静态存储区主要用于保存全局变量和静态局部变量
- 静态存储区的信息最终会保护到可执行程序中
小结
堆,栈和静态存储区是程序中的三个基本数据区
- 栈区主要用于函数调用的使用
- 堆区主要用于内存的动态申请和归还
- 静态存储区用于保存全局变量和静态变量
程序内存
程序与进程
- 程序与进程的不同
- 程序是静态的概念,表现形式为一个可执行文件
- 进程是一个动态概念,程序由操作系统加载运行后得到进程
- 每个程序可以对应多个进程
- 每个进程只能对应一个程序
程序的内存布局
- 各段的作用
- 堆栈段在程序运行后才正式存在,是程序运行的基础
- .bss段存放的是未初始化的全局变量和静态变量
- .text段存放的是程序中的可执行代码
- .data段保存的是已经初始化了的全局变量和静态变量
- .rodata段存放程序中的常量值,如字符串
小结
- 程序源码在编译后对应可执行程序中的不同存储区
- 程序和进程不同,程序静态概念,进程动态概念
- 堆栈段是程序运行的基础,只存在于进程空间中
- 程序可执行代码存放于.text段,是只读
- .bss和.data段用于保存全局变量和静态变量
内存操作经典问题
野指针
- 指针变量中的值是非法的内存地址,进而形成野指针
- 野指针不是NULL指针,是指向不可用内存地址的指针
- NULL指针并无危害,很好判断,也容易调试
- C语言中无法判断一个指针所保存的地址是否合法
野指针的由来
- 局部指针变量没有被初始化
- 指针所指向的变量指针之前被销毁
- 使用已经释放过的指针
- 进行错误的指针运算
- 进行了错误的强制类型转换
基本原则
- 绝不返回局部变量和局部数组的地址
- 任何变量在定义后必须0初始化
- 字符数组必须确认0结束符后才能称为字符串
- 任何使用与内存操作相关的函数必须指定长度信息
常见内存错误
- 结构体成员指针未初始化
- 结构体成员指针未分配足够的内存
- 内存分配成功,但并未初始化
- 内存操作越界
内存操作的一些规则
- 动态内存申请之后,应该立即检查指针值是否为NULL,防止使用NULL指针
int* p = (int*)malloc(56);
if(p != NULL)
{
}
free(p);
- free指针之后必须立即赋值NULL;
int* p = (int*)malloc(56);
free(p);
p = NULL;
if(p != NULL)
{
}
- 任何与内存操作相关的函数都必须带长度信息
小结
- 内存错误的本质源于指针保存的地址非法
- 指针变量未初始化,保存随机值
- 指针运算导致内存越界
- 内存泄漏源于 malloc和free不匹配
- 当malloc次数多余free时,产生内存泄漏
- 当malloc次数少于free时,程序可能崩溃