[C语言]内存管理
0x00 基本概念
内存管理,是指软件运行时对计算机内存资源的分配和使用技术。其目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。
内存分配方式:
- 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在整个运行期间都存在。例如全局变量,static变量。
- 从栈上创建。在执行函数时,函数内局部变量的存储变量都可以在栈上创建,函数执行结束时这些内存单元自动释放。栈内存分配运算内置于处理器的指令集中,效率很高,但分配的内容容量有限。
- 从堆上分配,亦成为动态分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但需要处理的问题也多。
内存分区:
- BBS段:未初始化的全局变量和静态变量。
- 数据段:已初始化的全局变量和静态变量、字符串常量等。
- 代码段:程序执行代码的一块内存区域。
- 堆:动态分配的内存段。
- 栈:存放程序临时创建的局部变量。
0x01 内存分配
为什么要进行动态内存分配?以数组元素为例,一个数组元素是存储于内存中连续的位置,当数组被声明时,其所需要的内存在编译的时候已经被分配。但在某些时候使用这种静态分配方式是不方便的,例如使用数组来存储班级中所有学生的成绩,但是不同的班级学生数量不同,这种情况下就无法确定数组到底分配多大内存。
常见动态内存分配、内存分配函数:
malloc
原型:extern void *malloc(unsigned int num_bytes);
头文件:stdlib.h
说明:在内存堆区分配一个大小为num_bytes的连续空间,如果分配内存成功,返回新分配内存的首地址,否则返回NULL。
用法实例:
int *p;
p = (int *)malloc(10*sizeof(int)); //分配4个字节的空间
calloc
原型:void *calloc(size_t n, size_t size);
头文件:stdlib.h
在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
用法实例
int *p = (int *)calloc(10,sizeof(int));
realloc
用法:指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)
头文件:stdlib.h
说明:给已经存在的内存空间扩充大小
0x02 野指针
野指针指指向一个已删除的对象或未申请访问受限内存区域的指针。与空指针不同,野指针无法通过简单地判断是否为 NULL避免,而只能通过养成良好的编程习惯来尽力减少。对野指针进行操作很容易造成程序错误。——[百度百科]
野指针产生的主要原因:
- 指针变量未初始化
- 指针释放后未置空
- 指针操作超过变量作用域
避免野指针的方法:
- 初始化时置NULL
- 释放时置NULL
0x03 内存泄露
内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。——[百度百科]
内存泄露分类(发生方式):
- 常发性
- 偶发性
- 一次性
- 隐式
int main()
{
int *p = (int *)malloc(10 * sizeof(int));
p = (int *)malloc(20 * sizeof(int));
return 0;
}
以上代码很明显就会造成内存泄露,直接更改指针变量的指向,初始化时所分配的内存空间没有进行释放,它会随着程序的运行一直存在。
int main()
{
int *p = (int *)malloc(10 * sizeof(int));
free(p); //释放p所指向的内存空间
p = (int *)malloc(20 * sizeof(int));
return 0;
}
改成如下代码就不会发生内存泄露。如果p暂时不用重新指向其他空间,需要把p=NULL,不然会产生野指针。
0x04 总结
在动态分配内存的时候,需要养成编码习惯,尽可能的注意指针的指向,避免野指针、内存泄露的问题。