返回介绍

8.1.3 堆内存的重新申请分配

发布于 2025-04-11 22:33:04 字数 3082 浏览 0 评论 0 收藏 0

在堆中申请分配内存空间后,有可能需要调整内存空间的大小,即对堆内存进行重新申请分配。例如,之前在堆中申请分配的空间为 short 类型的大小,但现在却想存储一个 int 类型的数据,就需要将之前所申请分配的内存从 short 类型的大小扩大为 int 类型的大小;或者是之前在堆中申请分配了 1000 字节的内存空间,现在却只需要 500 字节,就需要将之前所申请分配的内存从 1000 字节缩小为 500 字节。

可以使用函数 realloc 对堆内存进行重新申请分配,该函数的原型为:

void *realloc(void *ptr, size_t size);

参数 ptr 是 void 类型的指针,指向堆中所申请分配的内存,应该是之前调用 malloc、calloc 或其他 realloc 函数所返回的指针。参数 size 为新的大小。函数的功能为将 ptr 所指向的堆内存空间调整为 size 大小。函数返回值为指向重新申请分配后的堆内存的指针,若重新申请分配失败,则为空指针。例如:

void *p = malloc(4);
p = realloc(p, 12);
free(p);

这段代码中,首先通过 malloc 函数在堆中申请分配 4 字节的内存空间,并由指针 p 指向该 4 字节内存;随后,再次调用 realloc 函数,以指针 p 作为第一个实参,整型常量值 12 作为第二个实参,这会对指针 p 所指向的堆内存进行调整,即将原先 4 字节的内存扩大为 12 字节内存。由于 realloc 函数的返回值被再次赋值给指针 p,因此,指针 p 此时指向的是堆中所分配的 12 字节的内存。最后,通过 free 函数所释放回收的即是堆中所分配的 12 字节的内存。

但是这段代码是有问题的,或者说是不健壮的。为什么呢?想要弄明白的话,就需要对 realloc 函数的重新分配规则有所了解。如这段代码中,是想将 4 字节的堆内存扩大为 12 字节,正常情况下,realloc 函数会在原 4 字节的内存的基础上,再将其之后的 8 字节内存合并到一起,组成一个 12 字节的内存空间,但这有一个条件,原 4 字节内存之后的 8 字节内存必须是空闲状态。若在这 8 字节内存中出现部分或全部被占用的情况,则 realloc 函数会重新寻找一块新的连续的 12 字节内存,并将原 4 字节的内存释放回收,返回指向新的 12 字节内存的指针;若是没有寻找到连续的 12 字节内存,则会返回一个空指针,原先的 4 字节内存并不会被释放回收。

由此可见,上段代码有内存泄漏的风险:假若在重新申请分配 12 字节内存时没有成功,则会返回空指针,而原先所申请分配的 4 字节内存并没有被释放回收。此时,指针 p 已是空指针,原先的 4 字节内存已经没有任何指针来指向,因此,无法通过 free 来进行释放回收,造成了内存泄漏。

想要杜绝内存泄漏的风险,可做如下修改:

定义一个临时指针 ptmp,用来接收 realloc 函数的返回值。若重新申请分配成功,则 ptmp 指向了新申请分配的 12 字节内存,然后,在 if 语句中将 ptmp 重新赋值给 p;若重新申请分配失败,则 ptmp 为空指针,if 语句的条件不成立,不会将 ptmp 重新赋值给 p,所以指针 p 依然指向原来的 4 字节内存。结果就是:无论重新申请分配是否成功,指针 p 都会正确地指向堆中内存,free 函数都会正确地将堆中内存释放回收,因此不会造成内存泄漏。

在使用 realloc 函数的时候,还有一些有趣的特例。

1.用 realloc 实现 malloc 的功能

若将 realloc 函数的第一个参数设置为 NULL,这样的 realloc 函数,功能就相当于 malloc 函数。例如:

void *p = realloc(NULL, 4);

在堆中申请分配 4 字节的内存空间,并将其首地址作为返回值初始化给 void 类型的指针变量 p。如果申请分配失败,则 realloc 函数的返回值为 NULL,即指针 p 为空指针。由于 realloc 函数的第一个参数为 NULL,所以,realloc 函数就不存在释放回收原堆内存的操作,只会申请分配新的内存空间,因此,这样的 realloc 函数与 malloc 函数的功能相似。

2.用 realloc 实现 free 的功能

若我们将 realloc 函数的第二个参数设置为 0,这样的 realloc 函数,功能就相当于 free 函数。例如:

void *p = malloc(4);
realloc(p, 0);

第一条语句中,通过 malloc 函数在堆中申请分配 4 字节的内存空间,并将首地址初始化给指针变量 p。第二条语句调用了 realloc 函数,并将指针变量 p 作为第一个参数,整型常量值 0 作为第二个参数。表示将指针 p 所指向的堆内存大小从 4 字节缩小为 0 字节,即将原来的 4 字节内存释放回收。因此,这样的 realloc 函数与 free 函数的功能相似。

最后需要说明的是,在调用 realloc 函数对堆内存进行重新申请分配时,若新内存大小小于原内存大小,即是缩小原内存空间的,多出的内存部分会被释放回收,而剩余内存中的数据保持不变。若新内存大小大于原内存大小,即是扩大原内存空间的,原内存空间中的数据不变,而多出的内存部分不会被初始化,其值是未确定的。例如:

程序代码中,首先通过 calloc 函数申请分配一个长度为 5 的 int 类型数组的堆内存空间,并用指针 p 指向该内存空间;然后,通过 for 循环给数组的各元素分别赋值为 10、20、30、40 和 50;接着,通过 realloc 函数将指针 p 所指向的内存空间扩大 1 倍,即变为一个长度为 10 的 int 类型数组的内存空间;最后,再通过 for 循环将数组的 10 个元素值打印输出。编译运行程序,结果如下:

10 20 30 40 50 1920234345 -788528943 58481 9250720 9240772

可见,打印结果中,前 5 个值不变,而后 5 个值为随机值。即原先内存中的数据保持不变,而新增加的内存中数据并不会被自动初始化为 0。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。