【C语言】动态内存管理(下)(realloc函数)

news2024/11/26 19:48:31

文章目录

  • 前言
  • 1. realloc
  • 2. realloc函数在调整空间时的细节
    • 2.1 针对情况1(realloc后面有足够的内存空间)
    • 2.2 针对情况2(realloc后面没有足够的内存空间)
    • 2.3 realloc函数使用的注意事项
    • 2.4 realloc的使用实例
    • 2.5 realloc函数的补充
  • 3.动态内存常见的错误
    • 3.1 对NULL指针的解引用操作
    • 3.2 对动态开辟的空间的越界访问
    • 3.3 对非动态开辟内存使用free释放
    • 3.4 使用free释放一块动态开辟内存的一部分
    • 3.5 对同一块内存空间多次释放
    • 3.6 动态开辟内存忘记释放(内存泄漏)
  • 4.总结

前言

在动态内存开辟(上)中我向大家介绍了malloc、calloc以及介绍动态内存常见的错误。那么在本文中,我将继续给大家介绍另一个非常重要且实用的动态内存管理的函数——realloc函数,以及再深入探讨一下free函数的使用细节,避免在使用动态内存函数时,由于不及时释放或者时胡乱释放所造成不必要的麻烦出现。

1. realloc

C语言中还有一个用于动态内存开辟的函数——realloc函数,其功能十分的重要。

为什么为会这么说呢?
这就要与realloc函数的功能挂钩了。大家不妨回想一下,无论是malloc或者是calloc申请的动态内存空间一旦申请了之后就无法再调整空间的大小(除非自己设置一小段代码,进行调整)。比如:有时我们发现过去申请的空间太小了,有时候又觉得申请的空间太大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的处理。realloc函数就可以做到对动态内存开辟的空间进行调整。

realloc函数的功能:调整动态开辟后的空间大小

realloc具体细节如下:
realloc
realloc

函数原型:void* realloc (void* ptr, size_t size)

  • ptr是指向要调整的内存起始地址
  • size是表示要内存调整之后总的大小,单位为字节
  • 返回值为调整之后内存空间的起始地址
  • 这个函数调整原内存空间的大小的基础上,还会将原来内存中的数据移动到新的空间中(也就是realloc申请的内存中)。
  • 不过要注意(记住):realloc函数在调整空间时,存在两种情况:
    • 情况1:原有空间之后有足够大的空间
    • 情况2:原有空间之后没有足够大的空间

2. realloc函数在调整空间时的细节

为了方便大家的理解,我用图解的方式再展示一遍realloc在调整空间时的情况。

realloc调整空间大小的情况

2.1 针对情况1(realloc后面有足够的内存空间)

当是情况1的时候,要拓展内存就直接在原有的空间之后直接追加空间,原来空间的数据不会发生改变。

一句话概括:realloc后面的空间足够的话,直接在其后面调整内存大小即可。

2.2 针对情况2(realloc后面没有足够的内存空间)

当是情况2的时候,原有空间之后没有足够多的空间时,拓展的方法是:在堆空间上寻找另一个合适大小的连续空间来使用。这样函数返回的是一个新的内存起始地址。

2.3 realloc函数使用的注意事项

由于上述的两种情况,realloc函数在使用时就得注意一些细节的问题:

#include <stdio.h>
#include <stdlib.h>
int main()
{
 	int *ptr = (int*)malloc(100);
 	if(ptr != NULL)
 	{
 		//业务处理
 	}
 	else
 	{
		 return 1; 
	}
 	//扩展容量
 
 	//代码1 - 直接将realloc的返回值放到ptr中
	ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
 
 	//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
 	int*p = NULL;
 	p = realloc(ptr, 1000);
 	if(p != NULL)
	{
		ptr = p;
 	}
 	//业务处理
 	free(ptr);
 	return 0;
}

我们一般使用realloc函数调整空间大小时,不会直接将要调整空间的起始地址作为接收realloc函数的返回值。因为,如果realloc函数在申请申请空间失败之后,会返回一个NULL值,也就说不仅realloc申请失败了就连之前申请的那块空间也找不到了。这就会造成严重的信息丢失的问题。

2.4 realloc的使用实例

题目:加入之前我创建的10个整型的数据空间不够用了,我想扩容到20个整形数据的大小,并插入数据。

int main()
{
	int* p = (int*)malloc(10*sizeof(int));
	if (p == NULL)
	{
		perror("malloc fail");
		return 1;
	}

	//向申请空间里面填入1~10的数据
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}

	//到这里,突然我想扩容了,扩大到20个字节的大小,另外在这些数据的后面接着填入11~20的数据
	//----------------------------------------
	int* ptr = (int*)realloc(p,sizeof(int)*20);//只有强制类型之后我们才能随意操作这块内存空间
	if (ptr == NULL)
	{
		perror("realloc fail");
		return 1;
	}
		p = ptr; //这步得记住

	//-----------------------------------------
	for (int i = 10; i < 20; i++)
	{
		*(p + i) = i + 1;
	}

	for (int i = 0; i < 20; i++)
	{
		printf("%d ", *(p + i));
	}

	free(p);
	p = NULL;

	return 0;
}

上述的一些代码的具体细节需要读者们养成良好的编程习惯。多敲多练。

2.5 realloc函数的补充

realloc的函数原型为void* realloc(void* ptr, size_t size)。可能有些脑洞大开的读者就会想,如果我给形参ptr的值为NULL会发生什么情况。

给realloc形参中的ptr赋值为NULL就相当于malloc。也就是说,

realloc(NULL,40) == malloc(40)

还有就是,realloc函数在调整空间时属于第2种情况的话,其会自动释放之前的那块空间,不需要我们再手动释放了。 这点细节希望读者们牢记。

3.动态内存常见的错误

相信有不少读者在刚接触到动态内存管理时,总是会用出花来,而这些情况往往也是导致程序出现崩溃的罪魁祸首。
那就让我们来看看,有多少种我们常见的动态内存的错误用法。

3.1 对NULL指针的解引用操作

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;//如果p的值为NULL,就会出现问题
	free(p);
}

3.2 对动态开辟的空间的越界访问

void test()
{
	int i = 0;
	int* p = (int*)malloc(10*sizeof(int));
	if(NULL == p)
	{
		exit(EXIT_FAILURE);
	}
	for(int i = 0;i <= 10;i++)
	{
		*(p+i) = i;//当i为10的时候就会出现越界访问的情况
	}
	free(p);
	p = NULL;
}

3.3 对非动态开辟内存使用free释放

void test()
{
	int a = 10;
	int* p = &a;
	free(p);//这个就是错误的,因为指针p指向的不是动态内存开辟的空间
}

3.4 使用free释放一块动态开辟内存的一部分

void test()
{
	int*p = (int*)malloc(100);
	p++;
	free(p);//此刻的p不再是指向动态内存的起始地址了
}

3.5 对同一块内存空间多次释放

void test()
{
	int *p = (int*)malloc(100);
	free(p);
	free(p);//重复释放
}

3.6 动态开辟内存忘记释放(内存泄漏)

void test()
{
	int* p = malloc(100);
	if(NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
	whlie(1);
}

忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间一定要及时释放,并且是正确的释放。

4.总结

在本文中,我们学习到了realloc的各种细节,以及动态内存常见的错误。希望读者们以后在写代码时尽量避免这些错误,否则程序就会陷入到水深火热之中。

结合动态内存管理(上)的内容,我们已经清楚的知道了动态内存开辟是怎么一回事了。总而言之,就是玩明白malloc、calloc、realloc、free这几个函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1941224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ubuntu安装mysql8.0

文章目录 ubuntu版本安装修改密码取消root跳过密码验证 ubuntu版本 22.04 安装 更新软件包列表 sudo apt update安装 MySQL 8.0 服务器 sudo apt install mysql-server在安装过程中&#xff0c;系统可能会提示您设置 root 用户的密码&#xff0c;请务必牢记您设置的密码。…

产线中有MES系统 还有安装SCADA的必要吗?

MES系统即制造执行系统&#xff08;Manufacturing Execution System&#xff09;&#xff0c;是一种面向车间层的管理信息系统&#xff0c;旨在通过信息传递优化从订单下达到产品完成的全过程管理。 MES可以为企业提供包括制造数据管理、计划排程管理、生产调度管理、库存管理、…

网路布线和数值转换

文章目录 信号的分类数字信息的优势双绞线分类双绞线标准与分类 光纤的特点光纤分为单模光纤和多模光纤 光纤接口双绞线的连接规范EIA/TIA-568A和568B 线缆的连接综合布线系统无线电波的传输方式 数制转换十进制转二进制计算机的数值 信号的分类 1.模拟信号 2.数字信号 数字信…

动态住宅IP和静态住宅IP使用上有差异吗?

在互联网连接的世界中&#xff0c;IP地址是我们识别和访问网络资源的关键。住宅IP地址&#xff0c;特别是动态住宅IP和静态住宅IP&#xff0c;是两种不同类型的IP分配方式&#xff0c;它们在使用和功能上存在显著差异。 1. IP地址的稳定性 动态住宅IP&#xff1a;这种IP地址是…

七月份信息课总结

总结 七月份信息课总结算法记录线性代数&#xff1a;数论&#xff08;这是信竞生和数竞生都最难跨出的一步&#xff09;&#xff1a;动态规划&#xff08;~~DP万岁&#xff01;&#xff01;&#xff01;~~&#xff09;组合数学&#xff08;恶心&#xff0c;但我很喜欢&#xff…

Python爬虫-淘宝搜索热词数据

前言 本文是该专栏的第70篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏之前,笔者有详细针对“亚马逊Amazon搜索热词”数据采集的详细介绍,对此感兴趣的同学,可以往前翻阅《Python爬虫-某跨境电商(AM)搜索热词》进行查看。 而在本文,笔者将以淘宝为例,获取…

Linux----Mplayer音视频库的移植

想要播放视频音乐就得移植相关库到板子上 Mplayer移植需要依赖以下源文件&#xff1a;(从官网获取或者网上) 1、zlib-1.2.3.tar.gz &#xff1a;通用的内存空间的压缩库。 2、libpng-1.2.57.tar.gz :png格式图片的压缩或解压库 3、Jpegsrc.v9b.tar.gz : jpeg格式图片的压…

linux配置podman阿里云容器镜像加速器

1.下载podman yum install -y podman systemctl status podman systemctl start podman 2.获取阿里云个人容器镜像加速器地址 访问阿里云官网&#xff1a;首先&#xff0c;您需要访问阿里云&#xff08;Alibaba Cloud&#xff09;的官方网站。阿里云官网的URL是&#xff1a;…

医疗器械维修人员培训的重要性及其操作策略

医疗器械维修人员培训的重要性及其操作策略是确保医疗器械持续、安全、有效运行的关键。以下是对其重要性的解释以及相应的操作策略&#xff1a; 医疗器械维修人员培训的重要性 确保患者安全 经过培训的维修人员能够更准确地诊断问题&#xff0c;及时修复设备&#xff0c;避…

【IntelliJ IDEA】一篇文章集合所有IDEA的所有设置

IntelliJ IDEA 是一款功能强大的集成开发环境&#xff08;IDE&#xff09;&#xff0c;其设置涵盖了多个方面&#xff0c;以满足不同开发者的需求和偏好。由于 IDEA 的设置相当复杂和详尽&#xff0c;这里无法在一篇简短的文章中详细介绍所有设置。然而&#xff0c;我可以提供一…

轻松掌握:两个方法合并ZIP分卷压缩包

在文件传输和存储过程中&#xff0c;为了绕过文件大小限制或优化下载速度&#xff0c;我们常常会将大型文件分割成多个较小的ZIP分卷压缩包。然而&#xff0c;在接收这些分卷后&#xff0c;如何正确地将它们合并回原始的ZIP文件&#xff0c;可能是许多用户面临的一个问题。本文…

LeetCode两数相加

给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式存储的&#xff0c;并且每个节点只能存储一位数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以 0 开头。…

JAVA JUC学习笔记

基础知识 1、进程和线程的对比 进程基本上相互独立的&#xff0c;而线程存在于进程内&#xff0c;是进程的一个子集进程拥有共享的资源&#xff0c;如内存空间等&#xff0c;供其内部的线程共享进程间通信较为复杂 同一台计算机的进程通信称为 IPC&#xff08;Inter-process …

前端面试 vue 按钮级的权限控制

方案一 按钮权限也可以用v-if判断 但是如果页面过多&#xff0c;每个页面页面都要获取用户权限role和路由表里的meta.btnPermissions&#xff0c;然后再做判断 这种方式就不展开举例了 方案二 使用自定义指令实现 按钮级的权限控制 思维导图 心就是自定义指令的书写 首先…

exo 大模型算力共享;Llama3-70B是什么

目录 exo 大模型算力共享 exo框架的特点 如何使用exo框架 注意事项 结论 Llama3-70B是什么 一、基本信息 二、技术特点 三、性能与应用 四、未来发展 exo 大模型算力共享 exo框架的特点 异构支持:支持多种不同类型的设备,包括智能手机、平板电脑、笔记本电脑以及高…

Java 集合框架:Java 中的双端队列 ArrayDeque 的实现

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 019 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

AI多模态实战教程:面壁智能MiniCPM-V多模态大模型问答交互、llama.cpp模型量化和推理

一、项目简介 MiniCPM-V 系列是专为视觉-语⾔理解设计的多模态⼤型语⾔模型&#xff08;MLLMs&#xff09;&#xff0c;提供⾼质量的⽂本输出&#xff0c;已发布4个版本。 1.1 主要模型及特性 &#xff08;1&#xff09;MiniCPM-Llama3-V 2.5&#xff1a; 参数规模: 8B性能…

初学Linux之常见指令(下)

初学Linux之常见指令&#xff08;下&#xff09; 文章目录 初学Linux之常见指令&#xff08;下&#xff09;1. echo 指令2. cat 指令3. more 指令4. less 指令5. head 和 tail 指令6. date 指令7. cal 指令8. which 指令9. alias 指令10. find 指令11. grep 指令12. zip 和 unz…

C++中如何高效拼接两个vector

在C编程中&#xff0c;vector是一种常用的数据结构&#xff0c;它代表了一个可以动态改变大小的数组。在实际开发中&#xff0c;经常需要将两个vector拼接在一起&#xff0c;形成一个新的vector。本文将详细介绍如何在C中拼接两个vector&#xff0c;并探讨不同方法的性能差异。…

初学51单片机之指针基础与串口通信应用

开始之前推荐一个电路学习软件&#xff0c;这个软件笔者也刚接触。名字是Circuit有在线版本和不在线版本&#xff0c;这是笔者在B站看视频翻到的。 Paul Falstadhttps://www.falstad.com/这是地址。 离线版本在网站内点这个进去 根据你的系统下载你需要的版本红线的是windows…