C动态内存分配

news2024/11/16 4:31:26

🎈对比一般内存分配和动态内存分配

🌈一般内存分配

int val = 20;
char arr[10] = {0};

特点:
①大小是固定的,一旦分配好就无法改变(数组必须指定大小后编译器才能分配空间)
②分配的空间具体放置什么类型的数据是在分配内存时确定好的

🌈动态内存分配

有时候数组需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了,这时候就只能试试动态存开辟了。

🎈动态内存函数

🌈一.malloc和free

☀️1.malloc函数功能参数介绍

在这里插入图片描述
功能:申请一块空间
参数:申请空间的字节数
返回值:void ∗ * 类型的。申请成功,返回申请空间的起始位置;申请失败,返回NULL
申请到的空间在内存中的位置:
在这里插入图片描述

🎀malloc使用时的注意事项

①申请到的空间是没有类型的,因此返回值是void ∗ * 类型的,需要强制转换为想要的类型
②不会初始化空间内容
③参数不可以是0,即不可以申请0字节的空间,这种行为是标准未定义的
④malloc只负责申请,不会将空间还给系统,除非程序结束,否则空间不会归还,因此需要free函数释放空间

☀️2.free函数功能参数介绍

在这里插入图片描述
①参数:与free配合使用的某个动态内存分配函数所申请空间的起始位置
②无返回值
(free是如何做到精准释放的呢?其实申请了不止指定大小个空间,在这些空间之前还多申请了一部分空间来存放该空间大小,通过该指示信息从而达到精准释放空间,这些在底层实现,不会暴露出来)

🎀free使用时的注意事项

①如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
例如下图错误操作:
在这里插入图片描述

②如果参数 ptr 是NULL指针,则函数什么事都不做。
③假设p指向通过malloc动态开辟的空间,当空间被free释放后,p就成了野指针,因此free完后还要将p指针置为空

☀️3.malloc函数使用模版

#include<stdlib.h>
int main()
{
	//步骤一:申请空间
	int* p = (int*)malloc(40);
	//步骤二:判断是否开辟成功(返回值是否为空指针)
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		printf("%d\n", *(p + i));
	}

	//归还空间,并将指针置为空
	free(p);
	p = NULL;

	return 0;
}

步骤一:申请空间
步骤二:判断是否开辟成功(返回值是否为空指针)
步骤三:开辟成功进行操作
最后:归还空间,并将指针置为空
运行结果:
在这里插入图片描述
说明malloc不会初始化空间

🎀malloc开辟空间失败(比如要开辟的空间太大)

将要开辟的空间改为INT_MAX(大约21亿)

int* p = (int*)malloc(INT_MAX);

运行结果:
在这里插入图片描述

🌈二.calloc和free

☀️1.calloc函数功能参数介绍

在这里插入图片描述
功能:在申请空间的基础上将申请到的每个字节都初始化为0
在这里插入图片描述

参数一:开辟多少个空间num
参数二:每个开辟的空间的大小size
num*size才是最终申请并初始化的字节数
返回值:和malloc一样

☀️2.calloc函数使用模版

#include<stdlib.h>
int main()
{
	//步骤一:申请空间
	int* p = (int*)calloc(10,4);
	//步骤二:判断是否开辟成功(返回值是否为空指针)
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		printf("%d\n", *(p + i));
	}

	//归还空间,并将指针置为空
	free(p);
	p = NULL;

	return 0;
}

运行结果:
在这里插入图片描述
说明calloc会初始化空间为0

🎀calloc开辟空间失败(比如要开辟的空间太大)

将要开辟的空间改为INT_MAX(大约21亿)

int* p = (int*)calloc(INT_MAX,sizeof(int));

运行结果:
在这里插入图片描述

🌈三.realloc

☀️1.realloc函数功能参数介绍

在这里插入图片描述
👻功能:有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的利用内存,可以用realloc函数对内存的大小做灵活的调整。
👻参数一:过去所申请空间的起始位置
👻参数二:想要调整成为多大的空间(注:不是空间扩大的量,而是扩大后空间整体的大小)
👻返回值:void ∗ * 类型的,具体有两种情况
①返回值是被调整之前的空间起始地址:把空间往小了调整,或往大了调整但调整前空间后面有足够大的位置能够连续存放下这个大空间,此时调整后的空间起始地址和调整前的一样
在这里插入图片描述

②返回值是一块新空间的起始地址:(只有往大了调整才会出现这种情况)调整前的小空间后面没有足够大的位置能够连续存放下大空间,系统只能舍弃当下的这个小空间,另找一处大空间将小空间的内容以及增加的空间一起连续存放。简单来说就是将旧空间释放掉,返回新空间的地址。
在这里插入图片描述

③基于②,当怎么找也找不到一块新的连续空间时,会失败,返回空指针
👻当参数一ptr传的是NULL时,此时realloc的功能和malloc一样

🎀realloc使用时的注意事项

不可以让p指针(p指针是原先监管由malloc或calloc申请的空间的指针)直接接收realloc的返回值,因为当realloc寻找新空间失败并返回空指针时,p也编变成空指针,此时不但没有得到新空间的地址,连原先malloc或calloc申请到的空间也监管不到了,此时之前由malloc申请到的空间处于即用不到又找不到的状态。该状态就叫内存泄漏。

☀️2.正确使用realloc的模版

用一个新的指针ptr接收realloc的返回值,当返回值不为NULL时再将ptr的值赋给原空间起始地址p

#include<stdlib.h>
int main()
{
	//步骤一:申请空间
	int* p = (int*)malloc(40);
	//步骤二:判断是否开辟成功(返回值是否为空指针)
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//增加一些空间
	int* ptr =(int*)realloc(p, 80);
	if (ptr != NULL)
	{
		p = ptr;
	}
	//增容成功
	int i = 0;
	for (i = 0;i < 20;i++)
	{
		printf("%d\n", *(p + i));
	}

	//归还空间,并将指针置为空
	free(p);
	p = NULL;

	return 0;
}

运行结果:
在这里插入图片描述

🌈四.总结

①所有动态内存分配函数都在stdlib.h头文件中
②所有动态内存分配函数分配到的空间都在堆区上
③接收所有动态内存分配函数的返回值都需强制转换
④动态申请的空间不会因为出作用域而自动销毁(返还给操作系统),只有两种方式销毁,即用free释放或程序退出

🎈常见动态内存错误

🌈一.对NULL解引用

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

出现场景:malloc或calloc申请空间失败,p就是空指针了,此时不可以解引用p并赋值
解决方法:先判断指针是否为NULL,不为空再释放

🌈二.对动态开辟空间的越界访问

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

申请了20个字节的空间,但访问了20*4=80个字节
注:malloc和calloc表示空间大小的参数是以字节为单位的

🌈三.释放一块动态开辟内存的一部分

在这里插入图片描述

此时p已不再指向动态开辟空间的其实位置

🌈四. 动态开辟内存忘记释放(内存泄漏)

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

👻在test函数中开辟了100字节的空间,但没有释放,出函数时指针变量p销毁了,但空间以及空间内的内容还存在,此时没有办法可以访问到这部分空间,造成了空间的流失(内存泄漏),只有退出程序后该部分空间才会返还给系统。
👻程序在执行完malloc、calloc或realloc后,但还没到free时提前跳出,此时也会造成内存泄漏。
👻遇到电脑内存逐渐减少,电脑崩了,重启后又好了,这种情况很可能是内存泄漏。

🌈五.对非动态开辟内存使用free释放,

在这里插入图片描述

🌈六.对同一块动态内存多次释放

在这里插入图片描述

🎈动态内存相关笔试题

🌈题目一:

请问运行Test 函数会有什么样的结果?

void GetMemory(char *p)
{
 p = (char *)malloc(100);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(str);
 strcpy(str, "hello world");
 printf(str);
}

👻1.运行结果:由于访问空指针而引发异常
在这里插入图片描述
👻2.错误分析:Getmemory传参穿的是str本身,而不是地址,不是传址调用,虽然p里最终放的是malloc申请到100个空间的起始位置,但str里放的还是NULL,strcpy不可以直接对空指针str解引用,最终程序会出错,也无法打印出hello world。没有释放由malloc申请的空间。
👻3.正确写法
①传址,即传&str,并用二级指针接收参数
②要释放空间并将指向空间的指针str置为空
在这里插入图片描述
👻注:printf(str)这样写是对的。公认正确的打印方式是printf(“hello world”),但本质上是将字符串"hello world"的首元素地址传给了printf函数。当指针变量str存放了了字符串的首元素地址后,将str传给printf函数也可以打印出该指针指向的字符串。

🌈题目二:

请问运行Test 函数会有什么样的结果?

char *GetMemory(void)
{
 char p[] = "hello world";
 return p;
}
void Test(void)
{
 char *str = NULL;
 str = GetMemory();
 printf(str);
}

👻1.运行结果:在这里插入图片描述
👻2.错误分析:p指向的空间出GetMemory函数就被销毁了,虽然str存放了p的值,但通过该地址找不到字符串对应的空间,此时str变量里存放的指针是野指针,顺着野指针访问到的空间内容当然看不懂。该问题属于返回栈空间地址问题
👻3.正确写法:
在这里插入图片描述
在GetMemory函数中的字符串p前加上static,使字符串作用周期变长,此时就可以通过指针访问到字符串并打印出字符串了

👻4.类似错误
在这里插入图片描述
也属于返回栈空间地址问题,解引用指针p得不到变量a的值,因为出函数时a就被销毁了。

🌈题目三:

请问运行Test 函数会有什么样的结果?

void GetMemory(char **p, int num)
{
 *p = (char *)malloc(num);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello");
 printf(str);
}

1.运行结果:可以打印出"hello"
2.错误分析:有内存泄漏问题。没有释放malloc申请的空间

🌈题目四:

请问运行Test 函数会有什么样的结果?

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

👻1.错误分析:虽然打印出了"world",但由于str指向的空间已经还给操作系统了,此处就是非法访问,程序有可能崩
👻2.正确写法:free释放完后立马将指针置为空,这样即使后面有关于str的操作,也不会产生什么坏影响

❤️感谢浏览,如有错误请及时指正哦!

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

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

相关文章

ffmpeg批量分割视频解决视频前几秒黑屏的问题解决

echo 请输入视频地址&#xff1a; set /p fp echo 请输入开始时间&#xff1a; set /p st echo 请输入结束时间&#xff1a; set /p et echo 请输入分片时间&#xff1a; set /p sgt echo 注意&#xff1a;循环范围参数要空格。 for /l %%i in (%st%, %sgt%, %et%) do call :aa…

【Spring AOP学习】AOP的组成 SpringAOP的实现和实现原理

目录 一、认识SpringAOP 1、AOP是什么&#xff1f; 2、AOP的功能 3、AOP的组成&#xff08;重要&#xff09; 二、SpringAOP的实现 &#x1f337;1、添加Spring AOP框架支持 &#x1f337;2、定义切面和切点 &#x1f337; 3、定义通知 3.1 完成代码实现 3.2 具体通知…

目前可以实现用手机操作水质自动采样器吗

利用自动采样器进行水样采集可以说节省很大的人力物力&#xff0c;但是有时为了采到更具代表性的水样&#xff0c;我们需要对沟渠、深井、排污口等特殊场景进行采样。像这些狭小的空间领域采样就有点困难&#xff0c;对现场工作人员就带来了一些难题。所以也需要一款可以在井下…

HCIP——OSPF的防环机制

OSPF的防环机制 一、域间防环二、域内防环有向图转化1、有向图的画法2、示例&#xff1a; 三、SPF算法 OSPF将整个OSPF域划分为多个区域&#xff0c;区域内部通过拓扑信息计算路由&#xff0c;区域间传递路由信息&#xff0c;实现全网可达。OSPF防环机制主要是体现在域内防环和…

python3GUI--我的二维码生成工具By:PyQt5(附UI源码)

文章目录 一&#xff0e;前言二&#xff0e;展示1.主界面2.主界面-选择颜色&#xff08;动图&#xff09;3.主界面-选择样式&#xff08;动图&#xff09; 三&#xff0e;思路1.UI设计2.核心3.其他1.为什么调整了样式左侧二维码就跟着变化2.首次启动软件生成的文件哪里来的3.作…

华为8月8日将推出系统云翻新功能:P40/Mate 30系列首发

7月28日消息&#xff0c;7月28日消息&#xff0c;华为终端公司近日在微博上发布重要公告&#xff0c;宣布将于8月8日推出全新的系统云翻新功能。据悉&#xff0c;该功能将首次应用于华为 P40 系列手机和 Mate30 系列手机&#xff0c;为用户提供更便捷的手机数据备份和恢复体验。…

如何降低机场人员定位系统成本?哪种方案简单又好用?

一、方案简介 机场是一个室内空间巨大的人员聚集地差旅人员找不到出入口、卫生间、商铺为找检票口而误车等是很常见的现状。面对这些问题&#xff0c;机场信息数字化建设成为一种最为有效的解决方式&#xff01; 华安联大通过多技术融合实现室内外位置健全&#xff0c;推出一…

【机密计算-大厂有话说】微软 Azure

什么是机密计算&#xff1f; 机密计算是由机密计算联盟 (CCC) 定义的一个行业术语&#xff0c;CCC 是专注于定义并加速机密计算落地的基金会。 CCC 给机密计算的定义是&#xff1a;通过在基于硬件的可信执行环境 (TEE) 中执行计算来保护使用中的数据。 TEE 是是一个只能执行授权…

Jetbrains idea 代码关闭 注释自动渲染 导致换行不生效

方法1 关闭注释自动渲染 取消勾选 方法2 结尾使用 <br> 强制换行

【Python】logging模块笔记

目录 日志级别 四个组件 记录器 处理器 处理器 格式化器 格式 用法1&#xff1a;小项目可以采用编程的方法 用法2&#xff1a;建议采用配置文件的方式 用法3&#xff1a; 字典配置 日志级别 #默认的日志输出为warning # 使用baseConfig() 来指定日志输出级别 # 同时&#x…

每日一题——找到消失的数字

找到消失的数字 题目链接 思路 一个长度为n的数组中所有数据的范围在[1,n]内&#xff0c;题目要求我们找出在[1,n]范围内&#xff0c;但数组中没有出现的数字 如果可以使用额外空间&#xff0c;那这题就好办了。我们直接创建一个相同大小的数组&#xff0c;数组的每个位置代…

三. 多传感器标定方案(空间同步)--2

前面的内容&#xff1a; 一. 器件选型心得&#xff08;系统设计&#xff09;--1_goldqiu的博客-CSDN博客 一. 器件选型心得&#xff08;系统设计&#xff09;--2_goldqiu的博客-CSDN博客 二. 多传感器时间同步方案&#xff08;时序闭环&#xff09;--1 三. 多传感器标定方案…

AI聊天GPT三步上篮!

1、是什么&#xff1f; CHATGPT是OpenAI开发的基于GPT&#xff08;Generative Pre-trained Transformer&#xff09;架构的聊天型人工智能模型。也就是你问它答&#xff0c;根据网络抓去训练 2、怎么用&#xff1f; 清晰表达自己诉求&#xff0c;因为它就是一个AI助手&#…

【腾讯云 Cloud Studio 实战训练营】Cloud Studio实现健康上报小程序(代码开源)

目录 &#x1f373;前言&#x1f373;实验介绍&#x1f373;产品介绍&#x1f373;注册Cloud Stdio&#x1f373;后端Spring服务&#x1f373;创建项目上传项目数据库连接与导入 &#x1f373;Vue后台管理创建项目编辑模板信息选择环境镜像上传资源文件 &#x1f373;小程序⭐总…

容器演进时间轴及容器技术演进

1.1 1979年 — chroot 容器技术的概念可以追溯到1979年的UNIX chroot。 它是一套“UNIX操作系统”系统&#xff0c;旨在将其root目录及其它子目录变更至文件系统内的新位置&#xff0c;且只接受特定进程的访问。 这项功能的设计目的在于为每个进程提供一套隔离化磁盘空间。 …

NIM游戏/SG函数

NIM游戏 先看一下一维 NIM游戏。 有一堆大小为 n 的石子&#xff0c;甲和乙轮流从石堆里面拿石子&#xff0c;不能一次拿掉所有石子&#xff0c;取走最后一个石子的人获胜&#xff0c;甲先开始&#xff0c;谁是必胜的&#xff1f; 显然&#xff0c;谁先手&#xff0c;谁就获胜…

蓝桥杯单片机第五届国赛 真题+代码

onewire.c /* # 单总线代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础&#xff0c;根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求&#xff0c;进行代码调试和修改。 */// #include …

Opencv的Mat内容学习

来源&#xff1a;Opencv的Mat内容小记 - 知乎 (zhihu.com) 1.Mat是一种图像容器&#xff0c;是二维向量。 灰度图的Mat一般存放<uchar>类型 RGB彩色图像一般存放<Vec3b>类型。 (1)单通道灰度图数据存放样式&#xff1a; (2)RGB三通道彩色图存放形式不同&#x…

微服务性能分析工具 Pyroscope 初体验

Go 自带接口性能分析工具 pprof&#xff0c;较为常用的有以下 4 种分析&#xff1a; CPU Profiling: CPU 分析&#xff0c;按照一定的频率采集所监听的应用程序 CPU&#xff08;含寄存器&#xff09;的使用情况&#xff0c;可确定应用程序在主动消耗 CPU 周期时花费时间的位置…

数值线性代数:奇异值分解SVD

本文记录计算矩阵奇异值分解SVD的原理与流程。 注1&#xff1a;限于研究水平&#xff0c;分析难免不当&#xff0c;欢迎批评指正。 零、预修 0.1 矩阵的奇异值 设列满秩矩阵&#xff0c;若的特征值为&#xff0c;则称为矩阵的奇异值。 0.2 SVD(分解)定理 设&#xff0c;则…