【动态内存管理】

2023-11-17

前言

大家好,很高兴能和热爱知识的你在这里相遇。
下面我们在进入讲解之前我们可以先想一想什么是动态内存,
为什么会需要设置动态内存,
动态内存与我们的数组又有什么区别?


希望大家带着自己的想法进入下面的“副本”开始“刷 怪 升 级 ”。


一、动态内存开辟函数

(一)malloc

void* malloc(size_t size);
size为所开辟空间的字节数。
这个函数向内存申请一块连续的空间,并返回指向该内存的指针,
如果申请内存失败就返回一个NULL指针,因此对malloc函数的使用一定需要进行检测;
返回类型为 void* ,因此malloc函数并不知道开辟内存的类型,需要程序员在使用时进行强制类型转换;
如果size为0,malloc的行为是未定义的,结果取决于编译器。

int main()
{
	int* pa = (int*) malloc(sizeof(int) * 10);
	//开辟一块大小为40个字节的空间,也可以直接写成 malloc(40)
	return 0;
}

对于动态开辟的内存需要我们手动释放,释放操作就需要用到 内存回收函数。


(二)free

void free( void* ptr);
free是专门对动态开辟的内存进行释放和销毁的,
如果ptr不是动态开辟的内存,操作未定义,
如果ptr为NULL指针,函数就什么也不做。

int main()
{
	int* pa = (int*) malloc(sizeof(int) * 10);
	//开辟一块大小为40个字节的空间,也可以直接写成 malloc(40)
	free(pa);
	//释放pa所指向的内存空间
	return 0;
}

补充:肯定会有小伙伴有疑惑:我们没有对动态开辟的内存进行free好像也不会有任何问题,
没错,如果我们的函数走到最后也没有发现free函数,那么编译器会在结束程序时自动释放动态开辟的内存;
不过,如果我们总是这样做,把自己的事情交给编译器来做的话,那~编译器也不会那你怎么办的,
只是在程序运行结束之前我们动态开辟的内存是不能再次使用的,这样会造成不必要的内存浪费。


(三)calloc

void* calloc(size_t num, size_t size);
num为元素个数,size为一个元素的大小
calloc和malloc的区别就是calloc会将分配的内存全部初始化为0,而malloc只是将内存分配给我们,里面的值还是随机值(或者垃圾值)

int main()
{
	int* pa = (int*) calloc(10,sizeof(int));
	//同样使用free释放空间,后面的realloc同理
	free(pa);
	return 0;
}

补充:想要使用malloc达到和calloc一样的效果也很简单,可以使用memset函数将malloc开辟的空间全部设置为0即可。

(四)realloc

void* realloc(void* ptr, size_t size);
realloc函数使动态内存管理更加方便,
当我们开辟的内存过大或过小时都可以使用realloc函数进行调整。

int main()
{
	int* pa = (int*)malloc(40);
	//如果我们觉得空间太小,就可以对它进行扩容
	int* pb = (int*)realloc(pa, 80);
	//这里新创建一个指针pb 是为了防止空间开辟失败,使得pa之前存储的数据也丢失
	if(pb == NULL)
		return 1;
	else
		pa = pb;
	return 0;
}

补充1:realloc如果开辟内存成功就会自动free掉pa之前指向的空间,所以不需要我们再次free
补充2:realloc在进行扩容时,如果之前开辟的内存区域后面的空间足够,就会直接在原位置之后追加,
否则就需要重新开辟一块连续的空间,并将之前空间内的数据复制到新的空间。

图解:在这里插入图片描述


二、常见错误

(一)对NULL指针解引用

int main()
{
	int* pa = (int*)malloc(40);
	//如果内存开辟失败函数会返回空指针,后面对空指针解引用时会有问题
	//if(pa == NULL)
	//{
	//	printf("开辟内存失败\n");
	//	return 1; // 和return 0;作区分
	//}
	for(int i=0;i<10;i++)
	scanf("%d",pa+i);
	
	for(int i=0;i<10;i++)
	printf("%d ",pa[i]);
	
	return 0;
}

(二)对动态开辟空间的越界访问

int main()
{
	int* pa = (int*)malloc(40);
	if(pa == NULL)
	{
		printf("开辟内存失败\n");
		return 1; 
	}
	//这里越界时编译器可能也不会进行报错
	for(int i=0;i<12;i++)
	scanf("%d",pa+i);

	return 0;
}

(三)对动态开辟内存多次free

int main()
{
	int* pa = (int*)malloc(40);
	if(pa == NULL)
	{
		printf("开辟内存失败\n");
		return 1; 
	}
	for(int i=0;i<10;i++)
	scanf("%d",pa+i);
	free(pa);
	for(int i=0;i<10;i++)
	printf("%d ",pa[i]);
	free(pa);//这里对pa进行了两次free
	return 0;
}

(四)未在初始位置进行free

int main()
{
	int* pa = (int*)malloc(40);
	if(pa == NULL)
	{
		printf("开辟内存失败\n");
		return 1; 
	}
	for(int i=0;i<10;i++)
	scanf("%d",pa++);// pa 指针已经向后移动了
	free(pa);
	return 0;
}

(五)对非动态开辟内存进行free

int main()
{
	int pa[10]={0};
	for(int i=0;i<10;i++)
	scanf("%d",pa+i);
	free(pa);//pa为数组
	return 0;
}

(六)内存泄漏

int main()
{
	int* pa = (int*)malloc(40);
	if(pa == NULL)
	{
		printf("开辟内存失败\n");
		return 1; 
	}
	for(int i=0;i<10;i++)
	scanf("%d",pa+i);
	//free(pa);
	//就是不对pa指向的空间进行释放
	return 0;
}

三、柔性数组

柔性数组是包含在结构体之中的,
1.结构中柔性数组前至少有一个成员,
2.一个结构中只能有一个柔性数组,并且柔性数组需要放在最后,
3.对结构求大小时不包含柔性数组的大小,
4.包含柔性数组的结构需要使用malloc函数分配内存,并且分配的内存应该大于结构体的大小,来适应柔性数组的预期大小。

typedef struct str
{
	int data;
	char arr[];
}s;

int main()
{
	printf("%d\n",sizeof(s));
	s* ps = (s*)malloc(sizeof(s) + 100);
	//这里给arr分配了100个字节
	free(ps);
	return 0;
}

在这里插入图片描述


总结

下面我们来解答一下前面的几个问题:
1.什么是动态内存?
答:由于堆空间只能在程序运行时被使用,因此堆空间也被称动态内存。另外,动态内存只能在程序运行时通过指针
对分配给各种变量、字符串和数组( 只能通过指针来访问堆空间)。动态分配内存需要使用函数:malloc(),calloc()和realloc().
使用完后还要使用free()函数将内存归还。
2.为什么会需要设置动态内存?
答:在很多情况下我们并不能预先知道需要多大的空间,如果我们一开始就给定数组,那就会出现给的太多,或者空间不足的情况,
而有了动态内存分配,可以让我们需要多少空间就开辟多少,避免了内存浪费和内存不足的情况。
3.动态内存与我们的数组又有什么区别?
答:我觉得第二个的答案也同时适合本题(哈哈,熊猫国庆节也想偷个懒)。

以上就是动态内存相关的简单内容,如果有什么疑问或者建议都可以在评论区留言,感谢大家的支持,欢迎来评论区一起探讨,大家的鼓励是在这里插入图片描述继续更新的巨大动力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【动态内存管理】 的相关文章

随机推荐

  • 单链表的建立(C语言):头插法和尾插法建立单链表

    采用头插法建立单链表 该方法从一个空表开始 生成新结点 并将读取到的数据存放到新结点的数据域中 然后将新结点插入到当前链表的表头 即头结点之后 如图2 4所示 图2 4 头插法建立单链表 头插法建立单链表的算法如下 LinkList Cre
  • KMP算法是怎么被设计出来的

    定义 我们假设要在主串中寻找子串出现的所有位置 我们记主串中的开始位置为匹配位置 如在 abc 中匹配 bc 则匹配位置为 2 暴力 我们把匹配过程拆解为 枚举匹配位置 验证主串从匹配位置开始是否一一匹配子串 以此 有显然的 O n m
  • layui table 列覆盖

    layui table 列个数超过10列后 表头发生覆盖现象 临时解决方法 注释掉下面代码 if left div class layui table fixed layui table fixed l div class layui ta
  • DevC++小白程序-一元二次方程

    DevC 小白程序 一元二次方程 include
  • 智能化应用爆发,银行如何打造敏捷响应的智能中枢?

    在宏观经济增速放缓和互联网金融发展的双重压力下 银行已普遍开始了数字化转型 并向智能化方向迈进 不过在智能化转型过程中 银行也普遍面临数字资产建设能力不足 数据治理和AI模型开发自动化程度低等问题 银行需要搭建起适应未来的敏捷响应的智能中枢
  • js textarea焦点事件,CodeMirror焦点事件

    1 效果图 2 功能说明 当我把光标放在 指标对象 的控件框的时候 双击 字段名称 则字段数据写入指标对象控件框 当我把光标放在 指标维度 的控件框的时候 双击 字段名称 则字段数据写入指标维度控件框 当我把光标放在 指标sql 的控件框的
  • ElementUI表格错位问题

    最近一个项目需要用到表格 为了图省事直接用的ElementUI组件 使用默认的参数在表格所在的容器大小变化时会发生错位问题 如下图所示 网上的一些方法基本都试过了 没啥作用 可能是方式不对 最后查询ElementUI文档 在el table
  • 开关稳压电源设计

    文末下载完整资料 摘要 本设计应用隔离型回扫式DC DC电源变换技术完成开关稳压电源的设计及制作 系统主要由整流滤波电路 DC DC变换电路 单片机显示与控制电路三部分组成 开关电源的集成控制由脉宽调制控制芯片UC3843及相关电路完成 利
  • linux编程第一部分总结

    C 多线程安全原则 对象析构很复杂 我们采用shared ptr和weak ptr来做 enable shared from this lt gt 是用来做回调的 因为多线程中可能对象的生命周期比传出去的this指针短 同时为了不延长对象的
  • 碎片笔记

    前言 与传统的AI攻防 后门攻击 对抗样本 投毒攻击等 不同 如今的大模型攻防涉及以下多个方面的内容 目录 一 大模型的可信问题 1 1 虚假内容生成 1 2 隐私泄露 二 大模型的安全问题 2 1 模型窃取攻击 2 2 数据窃取攻击 2
  • python-ue4-metahuman-nerf:我创造了一个数字人!!

    原文 https zhuanlan zhihu com p 561152870 目录 收起 1 准备工作 制作 MetaHuman 角色 1 1 创建 MetaHuman 角色 1 2 Quixel Bridge 下载 MetaHuman
  • android 恢复出厂设置流程分析,Android恢复出厂设置流程分析

    最近看恢复出厂的一个问题 以前也查过这方面的流程 所以这里整理一些AP framework层的流程 在setting gt 备份与重置 gt 恢复出厂设置 gt 重置手机 gt 清除全部内容 gt 手机关机 gt 开机 gt 进行恢复出厂的
  • unity 五种旋转方式localEulerAngles、eulerAngles、rotation、localRotation和Rotate的区别

    1 1 transform localEulerAngles 使用localEulerAngles进行旋转的时候 我们要使用transform localEulerAngles new Vector3 x y z 其中 new Vector
  • 矩阵 行列式的计算

    行列式要求 要计算行列式 那么这个矩阵一定是一个方阵 行列式性质 行列式转置后值不变 互换行列式中两行 值变为相反数 行列式中两行成比例 行列式为0 行列式中一行所有元素乘以一个数后加到另一行 行列式值不变 行列式的计算有很多方法 矩阵的行
  • GPT专业应用:自动撰写宣传稿

    图片由Lexica 生成 输入 Staff working on product promotion 宣传稿是指按照有关政策文件或相关精神 以宣传某种主张 某项工作 某件事情等为目的 为获得理解 支持而撰写的应用文 基本格式包含四个要素 分
  • React 初学 - 使用 If/Else 条件进行渲染以及使用 && 获得更简洁的条件- 个人笔记50

    MyComponent 的 state 中包含一个布尔值 用于跟踪是否要在 UI 中显示某个元素 按钮切换此值的状态 目前 它每次都呈现相同的 UI 用if else语句重写render 方法 如果display为true则返回当前标记 否
  • CARLA平台+Q-learning的尝试(gym-carla)

    接触强化学习大概有半年了 也了解了一些算法 一些简单的算法在gym框架也实现了 那么结合仿真平台Carla该怎么用呢 由于比较熟悉gym框架 就偷个懒先从这个开始写代码 项目地址 https github com cjy1992 gym c
  • 3、上台阶问题

    问题描述 有n级楼梯 有2种爬法 1次1级 2级 n级楼梯有多少种爬法 解决思路 n 1 1种爬法 n 2 2种爬法 n 3 第一次爬1级 有2种爬法 第一次爬2级 有1种爬法 共3种爬法 第n次爬楼梯 f n f n 1 f n 1 代码
  • 成功解决ubuntu-22.04的sudo apt-get update一直卡在【0% [Waiting for headers]】

    成功解决ubuntu 22 04的sudo apt get update一直卡在 0 Waiting for headers 问题描述 解决方案 问题描述 在下载安装包的时候一直卡在0 Waiting for headers 报错信息如下
  • 【动态内存管理】

    目录 前言 一 动态内存开辟函数 一 malloc 二 free 三 calloc 四 realloc 二 常见错误 一 对NULL指针解引用 二 对动态开辟空间的越界访问 三 对动态开辟内存多次free 四 未在初始位置进行free 五