目录
- 前言
- 一、为什么要有动态内存管理?
- 二、动态内存管理函数
- 1. 认识动态内存管理函数
- 2. 动态内存管理函数的使用
- 三、常见错误
- 1. 对NULL的指针解应用操作
- 2. 对动态开辟空间的越界访问
- 3. 对非动态开辟内存使用free释放
- 4. 使用free释放一块动态开辟内存的一部分
- 5. 对同一块动态内存多次释放
- 6. 动态开辟内存忘记释放(内存泄露)
前言
动态内存管理(动态内存分配),就是指在程序执行的过程中动态地分配或者回收存储空间的方法。
一、为什么要有动态内存管理?
- C/C++中,编写程序有时不能确定数组应该定义为多大,因此在程序运行时要根据需要从系统中动态地获得内存空间
- 动态内存管理不像数组等静态内存管理方法那样需要预先分配存储空间(这样会限定死空间大小),而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小
二、动态内存管理函数
1. 认识动态内存管理函数
- 动态内存管理是在堆区上进行内存空间的开辟,开辟到的是一块连续的空间
动态内存管理函数:
#include <stdlib.h> //头文件
void* malloc (size_t size); //size_t表示无符号整形:size是要申请的字节大小
void* calloc (size_t num, size_t size); //num是元素个数,size是每个元素的大小
void* realloc (void* ptr, size_t size); //ptr指向要再次分配内存的地方
void free (void* ptr); //ptr指向经动态内存管理过的地方
2. 动态内存管理函数的使用
- 以上认识梳理了函数形参之后,我们先来了解一下
malloc
的使用
- 申请
开辟空间是可能失败的,一旦失败malloc就会返回空值NULL
,所以使用这个空间前我们需要判定一下p是否为空值,若为空值则不再往下执行程序 - 使用
- 释放
free括号里直接放上p,这就是为啥使用的时候尽量不要改变p的指向;释放空间之后,相当于把空间还给了系统,这时就要给p赋上空值,避免成为野指针 - 初始化情况
可以看出malloc
在开辟成功后并不会对该空间进行初始化,得我们自己后续赋值
附上整体代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main()
{
int* p = (int*)malloc(10*sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(p+i) = i;
}
//释放
free(p);
p = NULL;
return 0;
}
加上#define _CRT_SECURE_NO_WARNINGS 1是因为某些编译器会提示strerror不安全,加上这个就忽略这个提示,但一定要注意,必须加在首行!
- 至于
calloc
的使用我就不赘述了,形参注意一下区别就行啦,
你可以认为calloc = malloc + memset
-
realloc
是在已经开辟了内存空间的基础上,继续进行开辟空间的操作
- 老规矩,先开辟内存空间,这里我继续用
malloc
申请40字节内存 - 扩容
==这里需要注意:==我们不能直接用p接收realloc
返回的地址,万一是空值呢?本来p还指向前面所开辟的空间,结果给弄没了。
所以我们先进行判断,不是空值的话再重新赋给p,而ptr起到临时的作用,使用不上了就给它赋空值 - 同样地,使用完后要释放
- 小细节
- 此时
realloc
的用法相当于malloc
- 就算
realloc
开辟成功,返回的地址可能也不是原来p的地址了
(1)此时malloc
申请的空间后面还有位置可以开辟,故返回的地址还是 p原来所指向的地址 - (2)此时
malloc
申请的空间后面没有位置可以开辟,故返回的地址不是 p原来所指向的地址,它会重新寻找一个能容纳80字节的空间,然后把原空间的内容拷贝过来,再自动把原空间释放掉
附上完整代码
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
return 1;
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
//增加空间
int* ptr = (int*)realloc(p, 80);
//当realloc开辟失败的是,返回的是NULL
if (ptr != NULL)
{
p = ptr;
ptr = NULL;
}
//释放
free(p);
p = NULL;
return 0;
}
三、常见错误
- 一般来说,
malloc
等在使用时,与free
配套使用就不会出错,但还是有需要注意的地方
1. 对NULL的指针解应用操作
- 此时若开辟空间失败,就会返回空值,对空值进行解应用
2. 对动态开辟空间的越界访问
- 铁汁们敲多了代码之后往往会昏头,忘了p是整形,+1就跳过4个字节,i < 40的话造成严重的越界访问
3. 对非动态开辟内存使用free释放
- 这也属于昏头了,这时会报出严重的错误,该exe执行文件直接卡死
4. 使用free释放一块动态开辟内存的一部分
- 此时也会造成严重错误
- 所以我们前面进行
malloc
开辟空间的时候才强调不要改变p指向的
5. 对同一块动态内存多次释放
- 以下也属于昏头了…要多细心
- 但如果你在每次
free
完之后及时把p置为空值,即使再次free
也不会报错,因为free
不会对空值进行任何操作
6. 动态开辟内存忘记释放(内存泄露)
- 第一种是你自己忘记用完之后释放了,会导致这部分空间一直被占用,循环往复最后造成内存泄露。
- 第二种是比如别人写了一个函数,开辟了动态内存,但是需要你用完这个函数的时候来释放,但你却忘了,也会造成内存泄露。假如是你写这样的函数,要备注让别人知道你开辟了动态内存