C语言----动态内存

        学到这里了,大家应该对C语言的了解跟深一层了吧。我们C语言写代码不能只局限于直接写代码。我们要了解C语言的内存分布,我们都知道C语言的内存是有堆区,栈区,静态区的。然后栈区是我们平常创建临时变量存储的地方,静态区是我们全局变量,和静态变量。堆区就是动态内存了,并且我们今天讲的就是关于堆区的相关知识大家看一张图片可能会更加了解一些。

动态内存

       当然我们学习动态内存首先要知道什么是动态内存,为什么会存在,怎么使用。

       什么是动态内存,大家可以先看,名字中有一动而且后面跟着一个内存这说明什么,说明它可以改变吧,改变内存,那怎么改变嘞,这就是我们的第三个问题,如何使用了。我们下来处理第二个问题,为什么会存在动态内存。其实这个我认为给大家举一个例子,大家可以感受的更加深刻一些

        我们可以看到我首先创建看了一个变量y是int类型的,有4个字节,那么至少在现在我们看到y是被确定为4个字节的无法改变的吧,那么我们想改变y的直接大小或者直接改为数组的话,是不是只能在这里重新写。然后我为什么需要改,那时因为y只有4个字节不能满足我的需要了,那么我肯定是写了一些代码才发现这个不能满足我的需求了才改的呀。那这些代码只要涉及到了我的y是不是就要跟着改变要,很麻烦吧,而且要是一个不小心有一个没改掉是不是就出现BUG了。然后就是下面的,大家可能会说那我先不写有多少字节,我先创建最后写完了,看需要多少,我在写多少不就行了,诶,刚开始看好像还可以,但是大家想想,我们现在才写多少行代码呀,我们可以最后计算,那要是我们工作了做项目,写几百行代码几千行代码,我们最好一个一个数,那岂不是又做了一遍呀,而且一个不小心又要找BUG。所以这个时候就体现出来动态内存的重要性了。我先申请一部分内存,要是不够我就在申请一点,但是以前的是不会消失的,我们只需要接着写就行了。用完了以后返回去,像借东西一样,我要用先借一点不够再借一点,用完了以后还回去。动态内存差不多就是这样。

malloc

        好,那么我们就像来看看我们动态内存的第一个相关的库函数malloc,如果依据前面的那个例子的话,这个就是我们借东西的借条,好比我们向图书馆借书,我们肯定要先写借记卡吧,这是我们借东西的前提。那么malloc就是我们向堆区借内存的借据,并且堆区很大方,只要我们借并且成功的话内存都是连贯的。好了我们大概知道了malloc的作用了,那么我们就来学习如何使用吧。

 void* malloc (size_t size);

      首次运用这个库函数我们需要的头文件是<stdlib.h>,接着如果开辟成功,则返回⼀个指向开辟好空间的指针。 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。其次是,我们开辟内存在运行的时候其实是无法看出是否开辟成功的,所以我们需要检验我们是否开辟成功。然后就是void*,为什么我们要用void*那时因为我们开辟内存,我们知道开辟的内存是类型的,但是系统不知道啊,在没运行前,那么系统只能用一个都可以接受的来接收了啊。那只有void了。接着就是size,这个是字节的意思。我们需要开辟多少个直接那么我们直接写就可以。好,那么我们来看一段代码:

int main()
{
	int arr[5] = { 0 };
	int* ptr = NULL;
	ptr = (int*)malloc(5*sizeof(int));
	if (NULL != ptr)//判断ptr指针是否为空
	{//不为空的话就工作
		int i = 0;
		for (i = 0; i<5; i++)
		{
			*(ptr + i) = 0;
		}
	}
	free(ptr);//释放ptr所指向的动态内存
	ptr = NULL;//是否有必要?
	return 0;
}

       当然这里的数组arr其实可以不用创建的,但重点是大家看到旁边的内存窗口,我们看的是ptr内存变化,我们开始创建了一个ptr的指针,并且是NULL。但是后来我们用malloc把它变成了一个20个字节的数组(这个大家应该很好理解吧,因为一个常量或者变量是无法存放几个元素的,只有数组可以),并且我们将数组全部变为0。那么这就是我们最开始学习的先申请开辟动态内存。

       也许大家会突发奇想如果我接0个字节会怎么样嘞。这个事情其实并没有明确的答案,这个只能看编译器了,因为这里本来是接书的,然后你在借记卡上写个我借0本书,干什么,搞事情啊,人家不打你就算好的了。所以这个无意义的代码,大家知道就是可以了,不要在日后的代码中写出这种无用反增烦恼的事情了。

注:这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。那么我们就要用指针来接收。

free

        大家看了上面的代码,不知道是否看见了在最最好几行看见free的身影啊。那这个是什么东西嘞,代码肯定是写有关系的代码嘛,如果无关的话,写它干嘛岂不是徒增烦恼。那么它是干什么的嘞。还是那个例子,我们向图书馆借了书,是不是要还啊,不换也许当时没什么问题,但要是过一段时间,被拉如黑名单了,是不是就有麻烦了。就算到时候还回去了,麻烦已经产生了,双方还是不开心啊。那么free就是我们还东西的动作。每当我们开辟了动态内存后,要还回去,怎么还,用free。但是free只有一个作用,那就是只能还动态内存,其他的不归它管,要是你强行叫它还的话,如果是同行,也是空间开辟的话,那么它会直接告诉你,这不是动态内存开辟的空间,我不认是错误的。但如果参数 ptr 是NULL指针,则函数什么事都不做。我们先看正常释放动态内存开辟后会有什么结果:

          我们可以看到虽然内存没有任何明显改变,但是它内部已经改变了,因为我们前面已经说过free是释放空间,但是我们ptr没有被销毁啊,还可以用它的地址啊。但是它内部已经被处理如果不小心用了ptr的话,岂不是就gg了。所以我们需要写ptr=NULL来证明ptr确确实实已经无法使用了。好,我们看过正常使用free了,我们来看看错误示范:

      我们改为释放一个数组,这里就报错了,是不是。那么我们看看如果释放的是NULL看看:

      这里我们创建了一个NULL,然后释放,因为它原本就是NULL,释放了的话,还是一样的啊,相当于没用啊,是吧。

注:大家不要忘了,借了东西一定要还,开辟了空间的话,一定要free,不然会有不可估量的后果。并且是申请了几个,就释放几个,free一次只能释放一个开辟空间指令。

calloc

       讲了这么多,我们知道了借东西要用malloc,还东西用free。是不是只能用malloc借嘞,其他人没用吗,我要是不想找它借岂不是没有其他途径借了。这个时候眼睛一撇看到了一个calloc,calloc的功能是为 num 个大小为 size 的元素开辟⼀块空间。但是calloc有一个问题,它喜欢把自己处理的事情变为0,只要是自己经手的都变为0。这是它的一个不好习惯。与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为0。

void* calloc (size_t num, size_t size);

       那我们学习过malloc了,calloc直接上代码吧,更方便:

int main()
{
	int *ptr = (int*)calloc(5, sizeof(int));
	if (ptr == NULL)
	{
		perror("realloc");
		return 1;
	}
	for (int a = 0; a <5; a++)
	{
		printf("%d ", *(ptr+a));
	}
	free(ptr);
	ptr = NULL;
	return 0;
}

        那么我们看过了这个运行结果后就应该了解了如果我们需要对申请的内存空间的内容要求初始化,那么可以很⽅便的使⽤calloc函数来完成任务。

realloc

         当我们学习过了calloc后,我们知道calloc申请空间要将空间的内容全部变为0,那很麻烦啊,每一次都搞为0,那么realloc就站出来说,找我吧,我行为习惯良好,你要什么我给你什么,不会改变内容的。并且可以在你用了其他的库函数后,就是不够想扩大内存的话找我。但是世界上看到没有十全十美的东西,realloc肯定也是,那么大家看看下面我们来看看realloc有哪些问题。

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

       那么我们首先看看realloc怎么用:ptr 是要调整的内存地址,size 调整之后新大小返回值为调整之后的内存起始位置。 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间。但大家不知道是否有想过我们借东西,对方也没有了,不够了,怎么办是吧。那么这里就有两种情况了,可以借出足够的空间,无法借出足够的。

状况一:足够。要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。

状况二:空间不够。都说世界上还是好人多啊,当空间不够的话,它去帮你找,谁有那么大的空间,叫它借给你。在堆空间上另找⼀个合适大小的连续空间来使⽤。这样函数返回的是⼀个新的内存地址

正常使用:上面我们写了realloc可能发生的两种特殊情况,那么接下来就正常搞了吧

int main()
{
	int *y = NULL;
	int arr[5] = { 0 };
	int* ptr = NULL;
	ptr = (int*)malloc(5*sizeof(int));
	if (NULL != ptr)//判断ptr指针是否为空
	{
		int i = 0;
		for (i = 0; i<5; i++)
		{
			*(ptr + i) = 0;
		}
	}
	ptr = (int *)realloc(ptr,8 * sizeof(int));
	if (NULL != ptr)
	{
		int i = 0;
		for (i = 0; i<8; i++)
		{
			*(ptr + i) = 1;
		}
	}
	free(y);//释放ptr所指向的动态内存
	ptr = NULL;//是否有必要?
	return 0;
}

     

        我们看到开始我们是创建的5个int,全部赋为0,后来我又重新为8个int并且全部赋为1。当然出于习惯我还是给realloc一个判断是否为NULL,这是习惯,虽然看起来麻烦,但是习惯了使用起来还是没什么的。

动态内存常见问题

         当我们学习了上面的4个库函数后,我们再来了解一些除了库函数引起的问题。

动态开辟空间的越界访问

        我们知道有多少钱就花多少钱嘛,当然也可以省钱,但是你不能超前消费啊。毕竟要还的呀。但是C语言是没有这种的吧,你超过了这个范围,那么就实打实的有问题呀,那我们看看越界访问会发生什么:

       我们可以看到,我申请了5个int类型的空间,但是我却打印了6个,是不是最后一个打印出来值是没想到的。所以我们在动态内存开辟的时候需要注意有多少空间就有多少空间,不要超过这个范围。

释放一部分

        这个大家看标题一个很容易理解,就是free释放的时候只释放了一部分,好比,我们借书,我们借了10本书,但是我只还了9本,但是我还有一本没还,是不是还是有问题呀。

       虽然哈,我看不懂这个英文但是大家可以明显看到我们是报错了,这就代表我们这个代码是有问题的,那我们与上一个代码相比,我们将ptr+a改为了ptr++就报错了。所以这可以侧面体现出前者优于后者吧。

多次释放

       看了标题大家可能会想,怎么会多次释放嘞,我开始释放了就是释放了嘛,谁显得没事干多搞一次是。虽然大家都可以想到这件事,但是我们也不能排除我们下代买太多,自己都忘了前面是否释放过了啊,是吧。这也是一个问题,当然我觉得要是大家实在爬忘记,可以在每次释放了,就设置一个断点,表明我直接在这里释放了。这样也算一个方法嘛。

动态内存忘记释放

       好了前面我们写的都是释放错误,但我觉得还是忘记释放是最多的,因为在刚开始的时候我们还没习惯每次用完动态内存后要写一个free。

注:动态内存常见的错误除了库函数使用错误就是忘记释放或者释放错误,所以希望大家每次使用动态内存后都不要忘记释放内存!!!

小结

1:malloc()和calloc()函数用法一样, 区别是calloc()会对所申请内存的每个字节初始化为0

2:malloc(), calloc(), realloc()申请的内存不再使用时 ,一定要用free()释放 ,否则会造成内存泄漏,并且不要释放错误。

       好了这就是鄙人想与大家分享的关于动态内存的拙见了。肯定还有很多不足之处,希望大家可以在评论区指出。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3.23项目:聊天室

1、 基于UDP的网络聊天室 项目需求&#xff1a; 如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息如果有人下线&#xff0c;其他用户可以收到这个人的下线信息服务器可以发送系统信息 服务器 #inc…

四年蓄势,TikTok决定硬刚

在X平台&#xff08;原推特&#xff09;上线的一则视频里&#xff0c;周受资看起来又焦急&#xff0c;又强硬。他的眉毛扭到了一起&#xff0c;完全不像去年那个在美国国会听证会上&#xff0c;接受了5小时高压问询&#xff0c;仍风度翩翩的跨国公司CEO。 “过去几年来&#x…

js数据流详细讲解

文章目录 单向数据流单向数据流示例: 双向数据流双向数据流示例: 延伸和扩展状态管理Redux 示例&#xff1a; 异步数据流异步操作示例&#xff08;使用 async/await&#xff09;&#xff1a; 数据转换和处理数据处理示例&#xff08;使用 lodash&#xff09;&#xff1a; 实时数…

解决大型多模态模型的幻觉问题,新方法AITuning助力AI更可靠

引言&#xff1a;多模态对话幻觉的挑战 在人工智能领域&#xff0c;开发能够通过视觉和语言等多种渠道与人类互动的通用助手是一个重要问题。受到大型语言模型&#xff08;LLMs&#xff09;如ChatGPT的显著成功的启发&#xff0c;研究社区对开发能够支持视觉-语言指令的多模态助…

力扣热门算法题 75. 颜色分类,76. 最小覆盖子串,77. 组合

75. 颜色分类&#xff0c;76. 最小覆盖子串&#xff0c;77. 组合&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.21 可通过leetcode所有测试用例。 目录 75. 颜色分类 解题思路 完整代码 Python Java 76. 最小覆盖子串 解…

六.排序nb三人组(快速排序)

目录 17-快速排序原理介绍 思路: 18-快速排序代码实现 19-快速排序代码实现2 缺点: 递归的限度: 17-快速排序原理介绍 思路: --先找一个变量把 5(第一个数) 存起来, (两个箭头分别是left , right) --左边有一个空位, 发现左边的位置是给比5小的值准备的. --找比5小的值…

校招应聘流程讲解

在整个应聘流程中&#xff0c;记得保持积极的态度、认真准备面试&#xff0c;同时也要对自己的能力和经验有清晰的认识&#xff0c;这样才能在竞争激烈的校园招聘中脱颖而出&#xff0c;成功获得心仪的工作机会. 1. 校招资源获取 想要参加校招&#xff0c;首先需要获取校招资…

ROS2从入门到精通0-3:VSCode 搭建 ROS2 工程环境

目录 0 专栏介绍1 Ubuntu下安装VSCode1.1 基本安装1.2 将VSCode添加到侧边栏 2 VSCode集成相关插件3 VSCode运行ROS2环境步骤3.1 安装编译依赖项3.2 创建工作空间和源码空间3.3 启动VSCode与配置 4 测试工程环境4.1 C版本4.2 Python版本 0 专栏介绍 本专栏旨在通过对ROS2的系统…

每日一题 --- 977. 有序数组的平方[力扣][Go]

今天这一题和昨天的知识点是一样的&#xff0c;就是双指针法。 题目&#xff1a; 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,1…

Java中调用由C/C++实现的本地库(JNI本地程序调用)

文章目录 背景介绍什么是JNI&#xff1f;什么是本地库&#xff1f;开发Java使用JNI本地库步骤 编写Java类实现JNI本地调用windows系统下编译动态链接库创建Java项目&#xff08;demo&#xff09;第一步&#xff1a;编写带有native的Java类第二步&#xff1a;javac生成NativeDem…

深度学习_微调_7

目标 微调的原理利用微调模型来完成图像的分类任务 微调的原理 微调&#xff08;Fine-tuning&#xff09;是一种在深度学习中广泛应用的技术&#xff0c;特别是在预训练模型&#xff08;Pretrained-Models&#xff09;的基础上进行定制化训练的过程。微调的基本原理和步骤如下…

CRM软件推荐2024:五款顶级产品解析,助您找到最佳选项!

一天之计在于晨&#xff0c;一年之计在于春。 2024年&#xff0c;民营经济发展继续壮大&#xff0c;这对于各行各业而言都是一种机遇挑战。企业想要规范化客户管理&#xff0c;实现销售增长&#xff0c;CRM软件仍然是一个不错的选择。在数字化时代&#xff0c;企业数字化转型已…

预防颈椎病,从职场健康做起

随着现代社会工作方式的转变&#xff0c;职场人士长时间伏案工作&#xff0c;颈椎病的发病率逐渐上升。本文将介绍一些实用的预防颈椎病的方法&#xff0c;帮助职场人士保持健康&#xff0c;提高工作效率。 一、了解颈椎病 颈椎病是指颈椎间盘退行性变及其继发性椎间关节病理性…

基于Python实现高德地图找房系统-爬虫分析

概要 针对大学毕业生对于工作地周边交通出行情况不了解、租房困难等问题,本文主要研究了厦门市的租房信息及地铁公交出行路线,利用Python爬虫爬取58同城上厦门市的租房信息,并进行处理分析,再通过高德地图API将房源信息展示在地图上,实现了基于高德地图API的租房地图。 关键词&…

基于Spring Boot技术的幼儿园管理系统

摘 要 随着信息时代的来临&#xff0c;过去的传统管理方式缺点逐渐暴露&#xff0c;对过去的传统管理方式的缺点进行分析&#xff0c;采取计算机方式构建幼儿园管理系统。本文通过课题背景、课题目的及意义相关技术&#xff0c;提出了一种活动信息、课程信息、菜谱信息、通知公…

Angular入门问题小本本

1、console.log打印object对象显示[object object] 解决方案&#xff1a;使用JSON.stringify console.log(JSON.stringify($rootScope.MaintainDeviceInfo));2、 State ‘goDiskManagement’’ is already defined 解决方案&#xff1a;同一个项目中&#xff0c;不能定义相同…

基于骨骼的动作识别的行动结构图卷积网络

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;基于骨骼的动作识别的行动结构图卷积网络1、研究背景2、方法提出3、关键结构3.1、A-links inference module (AIM)3.2、Structura…

MyBatis3源码深度解析(二十)动态SQL实现原理(一)动态SQL的核心组件

文章目录 前言第八章 动态SQL实现原理8.1 动态SQL的使用8.1.1 \<if>8.1.2 <where|trim>8.1.3 <choose|when|otherwise>8.1.4 \<foreach>8.1.5 \<set> 8.2 SqlSource组件&BoundSql组件8.3 LanguageDriver组件8.3.1 XMLLanguageDriver8.3.2 Ra…

leetcode 20.有效的括号 JAVA

题目 思路 括号的匹配&#xff0c;这是一道经典的栈的应用问题。 给我们一个字符串&#xff0c;当我们遍历到左括号时&#xff0c;让其入栈。当我们遍历到右括号时&#xff0c;让栈顶元素出栈&#xff0c;看看栈顶的元素是否和遍历到的右括号匹配。不匹配的话直接false,匹配的…

vue2 脚手架

安装 文档&#xff1a;https://cli.vuejs.org/zh/ 第一步&#xff1a;全局安装&#xff08;仅第一次执行&#xff09; npm install -g vue/cli 或 yarn global add vue/cli 备注&#xff1a;如果出现下载缓慢&#xff1a;请配置npm 淘宝镜像&#xff1a; npm config set regis…