堆内存管理
为什么使用堆内存
- 栈内存的大小是有限的(堆内存的大小受物理内存限制)。
- 栈内存中的数据释放是不受程序员控制的(函数结束后,属于它的栈内存就会被系统自动释放,它不适合长期存放数据)。
如何使用堆内存
- 堆内存无法与标识符建立对应关系(必须使用指针来指向堆内存)
- c语言中没有管理堆内存的语句(标准库提供了一套函数来管理堆内存)
- 堆内存的管理和释放由程序员手动操作(显式调用函数来管理)
C语言为内存的分配和管理提供了几个函数。这些函数可以在<stdlib.h>头文件中找到。
函数 | 描述 |
---|---|
malloc | void* malloc(size_t size); 功能:在堆区分配一块指定大小的内存空间,用来存放数据。 size:要申请的字节数 返回值:所申请到内存的首字节地址,需要使用指针来接收(如果size的值为0,则返回NULL) 注意:这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
free | void free(void* address); 功能:释放内存空间,只是把内存的使用权收回,部分内容还在(只象征性的破坏了内容开头的一部分)。 address:malloc的返回值,之前所申请的堆内存的首地址。 注意:1、内存释放后还能再继续访问(不产生段错误,可能会造成脏数据),但是在非法使用。2、内存释放后,指针要立即置空,否则指针就会成为野指针。3、一块内存不能连续释放两次,会出现堆奔溃。 |
calloc | void* calloc(size_t num, size_t size); 在内存中分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。 num:申请的次数 size:每次申请的字节数 返回值:所申请的内存的首地址。如果size或num为0,返回NULL。 注意:calloc的速度会比malloc慢,因此绝大数情况下不使用。 |
realloc | void* realloc(void* address, size_t size); 功能:调整已经申请到的内存的大小。(相当于拷贝了一块新内存,旧内存会被释放掉)。 address:内存的首地址。如果address为空,而size大于0,则相当于申请新内存。 size:把内存大小调整为size。如果address不为空,而size为0,则相当于释放内存。 返回值:调整后的内存的首地址,一定要重新接受,旧地址已经被释放。 |
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 从堆内存申请1个字节
int* p1 = malloc(1);
// 从1字节调整为4字节
realloc(p1,4);
// 从4字节调整为2字节
realloc(p1,2);
// 从堆内存申请4字节
int* p2 = realloc(NULL,4);
// 释放内存
realloc(p2,0);
// 释放内存后要将指针置空
p2 = NULL;
}
要想进行内存的初始化,可以使用以下函数:
函数 | 描述 |
---|---|
bzero | void bzero(void* s, size_t n); 头文件:<strings.h> 功能:把一块内存的所有字节设置为0(清理内存) s:要清理的内存的首地址 n:要清理的字节数 |
memset | void *memset(void *s, int c, size_t n); 头文件:<string.h> 功能:把一块内存的每一个字节都设置为c s:要清理的内存的首地址 c:字节中要设置的数据(-128~127) n:要设置的字节数 返回值:初始化后的内存首地址(方便链式调用)。 |
注意: void* 是一种无类型的指针
- 不能直接使用,不能通过这种指针变量解引用(必须先转为其他类型)
- 可以和任意类型的指针进行自动转换。
- void* 也叫万能指针,解决函数之间专递指针参数时类型不确定问题。
- GNU 认为 void* 和 char* 一样
使用堆内存要注意的问题
当程序结束时,属于它的资源都会被操作系统回收。
-
内存泄露:由于失误而忘记或无法释放堆内存,导致堆内存无法循环利用,从而每次都重新申请新的内存,可用的内存会越来越少。
- 如何定位内存泄露:
- 当程序运行后观察内存的使用情况,内存暴涨则检查循环中的申请内存。
- 检查在调用free前指针是否已经变成空指针。
- 检查条件、业务逻辑,保证free会被调用。
- 解决方法:
- 保护指针不被修改,使用const修饰堆内存的地址
- 每一个malloc必须有对应的free。
- 如何定位内存泄露:
-
**内存碎片:**已经被释放但不能被再次分配使用的内存。频繁的申请、释放内存,导致申请和释放的不协调,一部分内存无法被再次使用。内存碎片无法杜绝,只能尽量减少。
- 如何减少:
1、尽量少使用堆内存,栈内存可以解决的尽量使用栈内存。
2、尽量申请大块的内存。
3、不要频繁的申请和释放。
- 如何减少: