【C语言高阶篇】成为编程高手必学内容,程序中的动态内存分配我不允许还有人不会!

news2024/11/29 2:48:50

在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《快速入门C语言》《C语言高阶篇》

⛺️生活的理想,就是为了理想的生活!

文章目录

  • 前言
  • 💬 为什么存在动态内存分配
  • 💬 动态内存函数的介绍
    • 1️⃣ 动态内存函数 malloc
      • 💭 malloc 函数返回失败怎么办
      • 💭 malloc 是在哪里开辟空间的
      • 💭 malloc申请空间为0
        • 📆 malloc申请空间会主动释放嘛
    • 2️⃣ 动态内存函数 free
        • 💭 内存函数 free的错误使用
    • 3️⃣ 动态内存函数 calloc
    • 4️⃣ 动态内存函数 realloc
      • 💭 内存函数 free的参数说明
      • 💭 内存函数 free的3种使用情况
        • ✅情况一
        • ✅情况二
        • ✅ realloc如何减少空间
      • ✈️ 总结

前言

   🌈hello! 各位宝子们大家好啊,又是新的一天开始了,今天给大家带来的是动态内存规划这一章节!
   ⛳️我们在创建变量的时候大家都知道大小是固定,不够灵活。而动态内存分配可以改变这一现象!当我们需要多少就可以规划多少,而不需要时就可以释放掉,这样是不是就可以极大地避免了内存的浪费!
   📚本期文章收录在《C语言高阶篇》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

💬 为什么存在动态内存分配

  ⛳️在前面内容中我们学的开辟空间大多都是用数据类型直接创建空间。

  • 比如用整形开辟一个大小为4个字节的空间
  • 或者数组开辟一个连续的储存空间
  • 而这些临时变量大多都是存放在栈区的
    🔥 注:在前面C/C++中内存大致分的三个区域有讲过 《C/C++的三个内存区域》
int main()
{
	int a = 0;//在栈空间上开辟四个字节
	int arr[40]={0};//在栈空间上开辟40个字节
}

但是这的开辟空间的方式有两个缺点:

  • 数组空间申请多了,如果没有用完就会照成空间的浪费!
  • 空间开辟大小是固定的

所以像以前的空间开辟方法满足不了我们的需求,那么有没有我们想开辟多少空间就开辟多少,而当我们不想要的时候还可以释放!这个时候就需要动态内存开辟了!

💬 动态内存函数的介绍

  ⛳️ 而动态内存开辟就需要用到相关的函数分别是: malloc free calloc realloc 把这四个函数只要掌握就可以完全的掌握动态内存分配了,下面我们就详细给大家介绍介绍:

1️⃣ 动态内存函数 malloc

动态内存开辟的函数: malloc

void* malloc (size_t size);

这个函数向内存申请一块 连续可用 的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是 void* ,所以 malloc 函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数 size0malloc 的行为是标准是未定义的,取决于编译器。

⛳️ 好了malloc的使用方法给大家介绍了,接下来就是给大家介绍介绍这个这个函数如何使用:

  • 他们的库函数都是 #include <stdlib.h>
  • 所以使用的时候一定要记得加头文件哦!
#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	malloc(40);
	return 0;
}

我们都知道数组创建的空间是连续,而malloc申请的空间也是连续的但是malloc的空间是没有类型的。

  • 那么我们想像数组一样访问整形4个字节来访问怎么办呢?
  • 很简单我们把 malloc 的返回值类型强制转换为 int*
  • 整形指针接收 malloc 的返回值就可以
#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	int* p=(int*)malloc(40);
}

这样我们就可以和整形数组一样存放整形了,因为指针解引用每次也跳过4个字节

💭 malloc 函数返回失败怎么办

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

  • 如果开辟失败,就会给 p 返回NULL 空指针
  • 而我们一旦对空指针在进行访问不会,越界访问越界了嘛?
  • 而这是绝对不允许的,一旦越界就会导致程序崩溃⁉️
  • 所以我们加一段代码来保证程序的安全性
int  main()
{
	int arr[10] = { 0 };
	int* p = (int*)malloc(40);
	//开辟失败
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	return 0;

这样就就可以在开辟失败时及时避免错误,直接return返回让程序结束!

  • 这里开辟失败是,让库函数 perror 给我们提示一下
  • malloc 里面出现了什么错误!
  • 下面就给大家观察一下开辟失败是什么样的

📑图片展示:
在这里插入图片描述

⛳️ 大家看这里当我们申请的空间太大是开辟不了就会给我们返回空间不够的错误提示

  • ps:申请的空间一定要非常大不然测试就不会返回错误值的
  • 博主试了好几遍还以为是自己的代码问题结果是申请空间太小了

💭 malloc 是在哪里开辟空间的

⛳️我们都知道临时变量是存放在栈空间的,那么malloc申请的空间是哪里的呢?

📚 代码演示:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	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]);
	}
	return 0;
}

📑 代码结果:
在这里插入图片描述

  ⛳️这里打印的就是我们申请空间的值,但由于malloc函数并不会给我们初始化所以里面存放的都是随机值。

  • 那么这里面的动态内存分布到底是什么样呢?
  • 为什么里面全部都是随机值呢?
  • 这个图片来告诉你一切

在这里插入图片描述

  ⛳️我们动态内存分配都是在堆区开辟空间的, p 指针变量是在栈区里面开辟的空间里面。所以当malloc在返回时返回了起始地址然后我们用 p 接收了malloc申请空间的起始地址

  • 但是,malloc这个函数只返回起始地址并不进行初始化

💭 malloc申请空间为0

  ⛳️ 做为一个程序员我们在想要申请空间的时候肯定是已经知道,要申请多少空间。你又要malloc申请空间,又只申请0个空间,这种行为本来就是不合理,所以我们在使用malloc时要避免这种情况以免出现不必要的错误!

  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

📆 malloc申请空间会主动释放嘛

  ⛳️而malloc申请的空间,当程序退出时,才会还给操作系统,而当程序未结束时,动态内存申请的内存空间,是不会主动释放的。这样就会照成内存的浪费!

  • 这时就需要使用free来释放,我们申请的动态内存空间
  • 编程的好习惯是,每次使用完malloc都要使用free释放空间
  • 下面我们就来介绍一下free函数

2️⃣ 动态内存函数 free

  ⛳️C语言提供了另外一个函数 free ,专门是用来做动态内存的释放和回收的,函数原型如下:

  • void free (void* ptr);

free函数用来释放动态开辟的内存。

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。

⛳️ 好了free的参数详情给大家介绍了,接下来就是给大家介绍介绍这个这个函数如何使用:

📚 代码演示:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	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;
}

  ⛳️ 这就是 free 的使用方法了,是不是非常简单。只需要把我们指针变量 p 传给 free 函数,因为 p 里面存放了 malloc 申请空间的起始地址,那么为什么还要把 p 给置为空指针呢?

  • 因为我们虽然把指针p记录的动态空间给释放了
  • 但是p本身不会被释放,而p里面存放的地址就成 野指针
  • 这个情况是非常不安全的所以我们把它置为空!

💭 内存函数 free的错误使用

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

  • 这种行为是不被允许的,希望大家使用时注意!

📚 代码演示:

int main()
{
	int a = 10;
	int* p = &a;
	free(p);//err
	return 0;
}

📑 代码结果:
在这里插入图片描述

3️⃣ 动态内存函数 calloc

  ⛳️ C语言还提供了一个函数叫 calloccalloc 函数也用来动态内存分配。其实非常简单这个函数和 malloc 的功能是一样的,只不过会把申请的空间初始化为 0

函数原型如下:

void* calloc (size_t num, size_t size);
  • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

📜举个例子:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	int* p = calloc(10,sizeof(arr[0]));
	//开辟失败
	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;
}

📑 代码结果:

在这里插入图片描述

  ⛳️ 这里就不给大家过多描述了,这个函数和malloc的使用大致一样!好的习惯是每次申请的动态空间在用完的时候都要释放掉!

4️⃣ 动态内存函数 realloc

  ⛳️有人会说不是动态内存可大可小嘛?想要多少空间就要多少,不想要了就可以缩小!前面的函数只能开辟和释放并不能控制大小啊?下面我们就给大家介绍介绍realloc函数它就完美的实现了这些功能。

  • 而想熟练的使用realloc函数就得知道
  • realloc开辟内存的三种情况

realloc 函数函数原型如下:

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

💭 内存函数 free的参数说明

realloc函数的出现让动态内存管理更加灵活:
void* realloc (void* ptr, size_t size);

  • ptr 是要调整的内存地址
  • size 调整之后新大小
  • 返回值为调整之后的内存起始位置。
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间。

📚 代码演示:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	int* p = calloc(10,sizeof(arr[0]));
	//开辟失败
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", p[i]);
	}
	//增加空间
	realloc(p, 80);

	free(p);
	p = NULL;
	return 0;
}

这就是realloc的用法当我们说开辟的空间只有40个大小不够用了。那么就可以用realloc去增加为80个字节大小!

💭 内存函数 free的3种使用情况

✅情况一

⛳️ 第一种情况就是后面的连续空间足够,我们我们就会在后给连续的新开辟40个字节使其增加为80个字节大小!

  • 下面给大家看看看内存分布情况图

📚 代码演示:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	int* p = calloc(10,sizeof(arr[0]));
	//开辟失败
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", p[i]);
	}
	//增加空间
	realloc(p, 80);

	free(p);
	p = NULL;
	return 0;
}

📑图片展示:

这就是第一种情况的内存分布,当后面的空间足够时后给连续的新开辟40个字节使其增加为80个字节大小

在这里插入图片描述

✅情况二

  ⛳️当我们想用reaclloc增加空间时,但是后面空间不够了就会重新开辟新空间并将原来空间的内容拷贝到新空间,并且将旧空间释放掉.

在这里插入图片描述
这里我们思考一个问题,realloc也会返回失败那么就会返回NULL空指针!

  • 本来我p指针变量还维护40个字节的大小结果你给一个空指针
  • 那么我不仅新空间没开辟,旧空间也丢了,

这样就会造成内存泄漏的问题,所以我们在这里就不敢用p接收我们的realoc返回值,需要进行代码改进!

📚 代码演示:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	int* p = calloc(10,sizeof(arr[0]));
	//开辟失败
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//开辟成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", p[i]);
	}
	//增加空间
	int* ptr = realloc(p, 80);
	if (ptr != NULL)
	{
		p = ptr;
		ptr=NULL;
	}
	else
	{
		perror("realloc");
	}

	free(p);
	p = NULL;
	return 0;
}

这样就可以避免我们上面说的错误了,如何 realloc 开辟失败我们就不接收空指针。

  • if判断完了之后再决定接不接收就完美解决问题

✅ realloc如何减少空间

  ⛳️ 这个不就更加简单了嘛?前面说了我们realloc函数可以动态开辟空间可大可小!那么使动态内存变小不就是更加简单了嘛?直接把内存改小不就行了.

  • 下面就来演示一下如何使动态内存变小

📚 代码演示:

#include <stdio.h>
#include <stdlib.h>
int  main()
{
	int arr[10] = { 0 };
	int* p = calloc(10,sizeof(arr[0]));
	//开辟失败
	if (p == NULL)
	{
		perror("calloc");
		return 1;
	}
	//减少空间
	int* ptr = realloc(p, 20);
	if (ptr != NULL)
	{
		p = ptr;
	}
	else
	{
		perror("realloc");
	}

	free(p);
	p = NULL;
	return 0;
}

📑 代码结果:
在这里插入图片描述

⛳️这里就可看到我们把malloc申请的动态空间40个字节,改变成了20个字节!

✈️ 总结

✅ 归纳:
好了以上就是关于动态内存分配函数 malloc free calloc realloc 4个动态内存分配函数的全部用法了!
  malloc的介绍和使用方法
  free的介绍和使用方法
  calloc和malloc的区别
  realloc语句使用的2种情况
☁️ 把这些内存函数掌握完,你就可以完美的使用动态内存分配了快去试试吧!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

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

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

相关文章

西门子PLC上位机测试

上一篇我们讲了三菱PLC的数据通信方法&#xff0c;今天我们讲讲另外一个PLC巨头--西门子。 西门子有很多系列&#xff0c;今天讲到的是用S7协议的S71200。西门子同样提供了丰富的集成库&#xff0c;例如S7.NET&#xff0c;对于C#上位机开发&#xff0c;是非常容易的事情。 首…

API开发,机器人api二次开发

由于自身在机器人方面滚爬多年&#xff0c;尝试了很多次&#xff0c;选择了一个信任的工具 可以给有需要的朋友们借鉴一下 开发起来很方便&#xff0c;技术也已经挺成熟的了 贴一点简单的给大家看下呢 测试文档&#xff1a;https://www.wkteam.cn/ 简要描述&#xff1a; …

10.Ceph接口使用

文章目录 Ceph接口使用CephFS文件系统服务端添加mds服务创建存储池授权用户权限 客户端前期准备客户端挂载方式一&#xff1a;基于内核方式二&#xff1a;基于 fuse 工具 Ceph 块存储系统 RBD 接口服务端创建存储池和镜像管理镜像 客户端镜像挂载快照管理快照分层快照展平镜像的…

Ubuntu20.04升级到Ubuntu 22.04

升级Ubuntu到最新版本 执行如下命令将Ubuntu升级到最新的版本&#xff1a; $ sudo apt update && sudo apt upgrade -y升级完成后&#xff0c;重启系统 reboot重启成功之后&#xff0c;查看系统的当前版本 $ lsb_release -a最新版本应该是20.04.6&#xff0c;如下图…

JTS-Orientation方向计算

org.locationtech.jts.algorithm.Orientation 使用说明 用于计算基本几何结构(包括点三重体(三角形)和环)的方向的函数。方向是平面几何的基本属性。 Orientation.index(Coordinate p1, Coordinate p2, Coordinate q) 说明 计算q点处在p1点->p2点方向的左侧还是右侧,左侧…

9. selenium API 【万字】

目录 1. 元素的定位 1.1 css selector 1.1.1 id 选择器 1.1.2 类选择器 1.1.3 标签选择器 1.1.4 后代选择器 1.2 xpath 1.2.1 相对路径 索引 1.2.2 相对路径 元素 1.2.3 相对路径 通配符 1.2.4 相对路径 部分元素定位 1.2.5 相对路径 文本定位 1.3 应用&…

gazebo软件建立带摄像和红外功能的小车

背景&#xff1a; 为了方便调整摄像头的高度&#xff0c;我需要重新构建以下带小车的模型。小白分享来了。 目录 1. 先构建一个能跑的小车。 1.1 gazebo设计四个物体&#xff1a;车体三个车轮。组合然后构建好后在本地保存成小车模型。 1.2 打开本地文件添加plugin插件&…

转行软件测试成功的关键因素是什么?

三年前张伟是一名厨师,职高毕业&#xff0c;团队一共有5个人&#xff0c;大家各自负责自己的模块&#xff0c;整体上感觉相差不大&#xff0c;特别的团结稳定。 可是后来随着疫情的发生&#xff0c;对整个社会都产生了严重的冲击&#xff0c;饭店每天的订单是越来越少&#xf…

Spark 4:Spark Core 共享变量

广播变量 # coding:utf8 import timefrom pyspark import SparkConf, SparkContext from pyspark.storagelevel import StorageLevelif __name__ __main__:conf SparkConf().setAppName("test").setMaster("local[*]")sc SparkContext(confconf)stu_inf…

设计模式day03

01gradle极速安装与配置入门 下载6.8.2版本,配置环境变量 配置镜像仓库 给gradle安装目录下init.d文件夹&#xff0c;放一个init.gradle文件&#xff0c;内容如下&#xff1a; gradle.projectsLoaded {rootProject.allprojects {buildscript {repositories {def JCENTER_URL…

go语言计算推算心率算法 http服务

目的 为了计算心率和并且将心率计算作为http服务来运行 几种计算方法 1 基本数据 a&#xff09; hrv heart rate variability b&#xff09; 呼吸 2 傅里叶变换 计算频率 高频和低频 3 隐形马尔科夫 模型 hmm 重在于推测概率 根据最近的心率计算 4 神经网络计算 基本计算 …

APP外包开发原生和H5的对比

在开发APP的技术中&#xff0c;除了原生开发外也可以使用H5框架来开发。原生开发的特点是质量高&#xff0c;用户体验更好&#xff0c;但成本高&#xff0c;适用于对质量要求高的APP项目。H5框架的特点是通用性较强&#xff0c;对开发人员的要求相对较低&#xff0c;成本也低&a…

23款奔驰GLE350轿跑加装前排原厂座椅通风系统,夏天必备的功能

通风座椅的主动通风功能可以迅速将座椅表面温度降至适宜程度&#xff0c;从而确保最佳座椅舒适性。该功能启用后&#xff0c;车内空气透过打孔皮饰座套被吸入座椅内部&#xff0c;持续时间为 8 分钟。然后&#xff0c;风扇会自动改变旋转方向&#xff0c;将更凉爽的环境空气从座…

怎么批量查询快递单号的物流状态并导出查询数据

随着互联网技术的不断发展&#xff0c;电商行业想做大做强&#xff0c;售后环节一定要做到位。想要做好售后服务&#xff0c;需要借助一些技巧与手段。今天小编给大家安利一款软件&#xff1a;“固乔快递查询助手”&#xff0c;这是一款可以批量查询快递信息的软件&#xff0c;…

python_PyQt5开发股票日数据更新工具

写在前面&#xff1a; 该工具更新的股票日数据来自优矿&#xff0c;为了把股票日数据在本地存储一份&#xff0c;这就面临需要定期更新的需求&#xff0c;为此开发了这个工具。 定期更新的股票日数据特征&#xff1a; 1 旧股票日数据&#xff08;也就是上次更新的数据&#…

【机器学习】吴恩达课程2-单变量线性回归

一、单变量线性回归 1. 模型描述 监督学习的流程 & 单变量线性回归函数 代价函数&#xff1a;&#xff0c;其中 m 表示训练样本的数量 公式为预测值与实际值的差&#xff0c;平方为最小二乘法和最佳平方/函数逼近。 目标&#xff1a;最小化代价函数&#xff0c;即 2. 只…

PR模板-33组故障干扰文字标题动画 Motion Glitch Typography

Motion Glitch Typography包含33组故障干扰文字标题动画pr模板。不需要任何插件或脚本&#xff0c;只需点击几下&#xff0c;您的视频就有很酷的故障标题动画&#xff0c;适用于预告片、宣传片或任何类型的视频。 适用软件&#xff1a;Premiere Pro 2020 或更高版本 分辨率&a…

Go语言之函数补充defer语句,递归函数,章节练习

defer语句是go语言提供的一种用于注册延迟调用的机制&#xff0c;是go语言中一种很有用的特性。 defer语句注册了一个函数调用&#xff0c;这个调用会延迟到defer语句所在的函数执行完毕后执行&#xff0c;所谓执行完毕是指该函数执行了return语句、函数体已执行完最后一条语句…

密码学学习笔记(十四):SHA-3 Sponge Construction - 海绵结构

SHA-3算法满足了哈希函数的三个安全属性&#xff0c;并且和SHA-2的变体达到同样级别的安全性。此外&#xff0c;SHA-3算法不容易受到长度扩展攻击&#xff0c;并可用于计算秘密消息的哈希值。 SHA-3是一种建立在Permutation(置换)之上的密码算法。 置换就是假设有两个数组a和…

Hadoop第一课之环境配置

1.配置一个模板机 要求&#xff1a;IP DNS地址页 网址 防火墙 安装包 1.ip ifconfig 查询 先用虚拟机看一下自己的网关 vim search/provides 命令 查找 # 修改网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 如果提示找不到vim命令&#xff0c;使用yum下载v…