C语言动态内存讲解+通讯录2.0

文章目录

  • 前文
  • malloc和free
  • calloc
  • realloc
    • 枚举常量的简单说明及使用
  • 通讯录2.0
      • 动态开辟通讯录,满了就扩容
      • 保存数据和载入数据
    • 通讯录2.0演示
    • 推荐好用的软件

前文

本文主要介绍动态开辟的几个函数,以及改进之前的通讯录。

在这里插入图片描述

我们局部变量等是在栈区上开辟空间的,而我们动态开辟的空间在堆上面。

头文件: #include<stdlib.h>

函数介绍:
动态开辟空间的函数有以下四个,下面由我来一一介绍
malloc
calloc
realloc
free

malloc和free

malloc是开辟一块连续的空间

在这里插入图片描述

用法如下,需要注意的是,由于malloc是动态开辟内存的,所以我们需要判断是否开辟成功,如果是NULL就说明开辟失败了,malloc如果开辟失败了就会返回NULL;

free则是专门释放由动态开辟的空间的。

#include <stdlib.h>
int main()
{int* arr = (int*)malloc(INT_MAX);if (arr == NULL){perror("malloc arr");return 1;}int  i = 0;for (i = 0; i < 10; i++){arr[i] = i + 1;}for (i = 0; i < 10; i++){printf("%d ", arr[i]);}free(arr);arr = NULL;return 0;
}

在这里插入图片描述
下面我们通过调试来开一下free的过程
在这里插入图片描述

这里可以看到当我们malloc完后,arr就指向了一块有10个整型大小的连续空间

在这里插入图片描述
当我们free完之后发现虽然我们的空间已经被收回了,但是arr此时还是保留着那片空间的地址,此时arr就是野指针,不能继续使用,所以每当我们使用完动态开辟的空间free之后,我们都应该手动的把这块空间置为NULL

在这里插入图片描述
接下来验证以下malloc开辟失败的情况。
在这里插入图片描述
我们把malloc的参数设置为INT_MAX,INT_MAX表示整型所能容纳的最大整数,在x86的环境上,此时就是INT_MAX就是我们所拥有的最大空间,但不可能把所有的内存都给arr,所以开辟失败,此时arr指向NULL

calloc

calloc和malloc的区别就是calloc开辟的空间是初始化过了的。

在这里插入图片描述

同时calloc开辟的空间用完以后,也要free掉,同时置为NULL。

在这里插入图片描述

realloc

realloc是当空间大小不够的时候,可以在增加空间的大小,当然也可以减少空间

在这里插入图片描述
同时realloc开辟空间分两种情况如图:
在这里插入图片描述

#include <stdlib.h>int main()
{int* arr_malloc = (int*)malloc(40);if (arr_malloc == NULL){perror("malloc fail!");return 1;}int  i = 0;for (i = 0; i < 10; i++){arr_malloc[i] = i + 1;}int* ptr = (int*)realloc(arr_malloc, 80);if (ptr == NULL){perror("realloc fail!");return 1;}arr_malloc = ptr;for (i = 10; i < 20; i++){arr_malloc[i] = i + 1;}for (i = 0; i < 20; i++){printf("%d ", arr_malloc[i]);}free(arr_malloc);arr_malloc = NULL;return 0;
}

在这里插入图片描述
可以看到ptr直接拷贝了原空间的内容,此时后面的空间大小足够,所以直接在原空间后开辟。,用完不要忘记free和置NULL
在这里插入图片描述
此时来看一下如果后面空间不够是不是真的重新开辟了呢?
在这里插入图片描述
如图所示,当原空间后的空间不够的时候确实会重新开辟,并把原空间的内容拷贝过去。

到这里动态开辟的函数我们讲解就完毕了,前面我已经发过了文件的,接下来我们来对我们上次的通讯录进行改进

枚举常量的简单说明及使用

枚举顾名思义就是一一列举的意思,
把可能的值列举出来

比如在生活中有星期一~星期天,可以一一列举

在这里插入图片描述
枚举和结构体的用法类似,不过枚举里面成员变量使用逗号隔开的。同时枚举里面如果你不规定的话,第一个默认为0,而且你枚举出来的值是可以直接使用的,不需要像结构体一样通过.或着->去访问成员。

通讯录2.0

通讯录1.0版本地址

1.0版本问题:
1.没办法对数据进行保存
2.空间大小固定了,不能修改

解决方案:
1.每次退出程序的时候把数据保存到文件中,初始化的时候导入数据到内存中
2.空间动态开辟,同时给一个容量,每次满了,就扩容。

同时这个的switch选项我们可以使用枚举美观一下
在这里插入图片描述

enum
{EXIT,//0ADD,DEL,SEARCH,MODIFY,SHOW,SORT//6
};

在这里插入图片描述

动态开辟通讯录,满了就扩容

在这里插入图片描述
此时我们应该把上面的data改成指针形式而不是数组,同时定义每次扩容扩充几个

//定义容量
#define INC_SZ 3
//通讯录
typedef struct Contact
{int count;//表示当前人数PeoInfor* data;int capacity;}Contact;

接下来就是改变初始化函数了。
原函数
在这里插入图片描述
改完之后

//初始化通讯录
void InitialPeoInfor(Contact* pc)
{assert(pc);pc->count = 0;pc->capacity = INC_SZ;//我这里把容量初始化为INC_SZ,写几个都可以看你自己pc->data = (PeoInfor*)malloc(pc->capacity * sizeof(PeoInfor));if (pc->data == NULL){perror("malloc fail!");return;}
}

接下来就是扩容的问题了,扩容我们写在添加联系人这个函数里面

//检查容量
void check_capacity(Contact* pc)
{//当我们通讯录的人数跟容量相同了,就说明满了,此时需要扩容if (pc->count == pc->capacity){PeoInfor* ptr = (PeoInfor*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfor));if (ptr == NULL){perror("realloc fail!");return;}pc->data = ptr;pc->capacity += INC_SZ;//扩容后把容量加上扩容的数量printf("扩容成功\n");}
}

此时运行可以发现当我们输完3个人再想输入的时候会提示扩容成功。
在这里插入图片描述

既然我们的通讯录是通过动态开辟来的,那么我们肯定要free掉,所以我们再写一个销毁数据的函数。

//销毁数据
void DesTory(Contact* pc)
{assert(pc);free(pc->data);pc->data = NULL;
}

在这里插入图片描述

保存数据和载入数据

保存数据是当我们程序退出的时候保存,这里我们写一个SaveData()函数来实现保存数据

这里的数据我们以二进制的形式写入,这样就可以对数据进行一定的保护。
既然二进制的形式写入,我们来复习以下fwritefread这两个函数吧。
在这里插入图片描述

//保存数据
void SaveData(Contact* pc)
{assert(pc);//                           wb表示以二进制形式写入FILE* pf = fopen("data.txt", "wb");if (pf == NULL){perror("fopen fail!");return;}int i = 0;for (i = 0; i < pc->count; i++){fwrite(pc->data + i, sizeof(PeoInfor), 1, pf);}printf("保存成功\n");
}

同时载入数据的时候我们要对容量进行检查
在这里插入图片描述

//载入数据
void LoadPeoInfor(Contact* pc)
{FILE* pf = fopen("data.txt", "rb");if (pf == NULL){perror("Read data fopen");return;}PeoInfor tmp = { 0 };while (fread(&tmp, sizeof(PeoInfor), 1, pf) == 1)//每次读一个{check_capacity(pc);pc->data[pc->count] = tmp;pc->count++;}
}//初始化通讯录
void InitialPeoInfor(Contact* pc)
{assert(pc);pc->count = 0;pc->capacity = INC_SZ;pc->data = (PeoInfor*)malloc(pc->capacity * sizeof(PeoInfor));if (pc->data == NULL){perror("malloc fail!");return;}LoadPeoInfor(pc);
}

通讯录2.0演示

在这里插入图片描述

推荐好用的软件

这里推荐两款软件,一个就是我这个鼠标,他的图标变成了羽毛,还是挺方便找鼠标指针的。
第二个就是这个gif录制软件了,ScreenTOGif,真的挺好用的。
安装包放在文章顶部啦~。

鼠标那个我上传上去了,好像只能上传一个,gif录制我直接给下载链接吧。
ScreenTOGif官网

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

非wpf应用程序项目【类库、用户控件库】中使用HandyControl

文章速览 前言参考文章实现方法1、添加HandyControl包&#xff1b;2、添加资源字典3、修改资源字典内容 坚持记录实属不易&#xff0c;希望友善多金的码友能够随手点一个赞。 共同创建氛围更加良好的开发者社区&#xff01; 谢谢~ 前言 wpf应用程序中&#xff0c;在入口项目中…

Linux 给网卡配置ip

ip addr | grep eth9 ifconfig eth9 10.0.0.2 netmask 255.255.255.0 up

linux安装git

一、下载git 注意&#xff1a;不要下载最新版本的git&#xff0c;否则可能安装会失败&#xff0c;缺失很多依赖文件&#xff0c;解决起来费时费力&#xff0c;还可能不成功 尽量下载前几年的&#xff0c;甚至10年前的都可以 下载地址&#xff1a;https://mirrors.edge.kerne…

Codigger用户篇:安全、稳定、高效的运行环境(一)

在当今数字化时代&#xff0c;个人数据的安全与隐私保护显得尤为重要。为了满足用户对数据信息的安全需求&#xff0c;我们推出Codigger分布式操作系统&#xff0c;它提供了一个运行私有应用程序的平台&#xff0c;旨在为用户提供一个安全、稳定、高效的私人应用运行环境。Codi…

3.26号arm

1. SPI相关理论 1.1 概述 spi是一种同步全双工串行总线&#xff0c;全称串行外围设备接口 通常SPI通过4个引脚与外部器件相连&#xff1a; MISO&#xff1a;主设备输入/从设备输出引脚。该引脚在从模式下发送数据&#xff0c;在主模式下接收数据。 MOSI&#xff1a;主设备输…

Go-Gin-Example 第八部分 优化配置接口+图片上传功能

文章目录 前情提要本节目标 优化配置结构讲解落实修改配置文件优化配置读取及设置初始化顺序第一步 验证 抽离file 实现上传图片接口图片名加密封装image的处理逻辑编写上传图片的业务逻辑增加图片上传的路由 验证实现前端访问 http.FileServerr.StaticFS修改文章接口新增、更新…

以太网/USB 数据采集卡 24位16通道 labview 256K同步采样

XM7016以太网SUB数据采集卡 XM7016是一款以太网/USB高速数据采集卡&#xff0c;具有16通道真差分输入&#xff0c;24位分辨率&#xff0c;单通道最高采样率256ksps. 16通道同步共计4.096Msps、精密前置增益放大、集成IEPE/ICP硬件支持的特点。本产品采用了多个高精度24位ADC单元…

学习JavaEE的日子 Day32 线程池

Day32 线程池 1.引入 一个线程完成一项任务所需时间为&#xff1a; 创建线程时间 - Time1线程中执行任务的时间 - Time2销毁线程时间 - Time3 2.为什么需要线程池(重要) 线程池技术正是关注如何缩短或调整Time1和Time3的时间&#xff0c;从而提高程序的性能。项目中可以把Time…

【 MyBatis 】| 关于多表联查返回 List 集合只查到一条的 BUG

目录 一. &#x1f981; 写在前面二. &#x1f981; 探索过程2.1 开端 —— 开始写 bug2.2 发展 —— bug 完成2.3 高潮 —— bug探究2.4 结局 —— 效果展示 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 今天又是 BUG 气满满的一天&#xff0c;一个 xxxMapper.xm…

跑spark的yarn模式时RM连不上的情况

在linux控制台跑spark on yarn一个测试案例&#xff0c;日志中总显示RM连yarn服务的时候是&#xff1a;0.0.0.0:8032 具体情况如下图&#xff1a; 我问题出现的原因&#xff0c;总结如下&#xff1a; 1.防火墙没关闭&#xff0c;关闭 2.spark-env.sh这个文件的YARN_CONF_DIR…

MyBatis基础使用

MyBatis首页https://mybatis.net.cn/ MyBatis细节注意&#xff0c;让你更加熟悉MyBatishttps://blog.csdn.net/m0_61160520/article/details/137173558?spm1001.2014.3001.5501 1.项目目录 2.数据库 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_e…

Linux文件与进程交互的窥探者lsof

lsof 是一个 Linux 和 UNIX 系统中的实用工具,用于列出系统中打开文件的所有信息。这个名字代表 “List Open Files”,但它也可以显示进程相关的其他信息,如: 打开的文件描述符列表 打开网络连接的列表 被进程使用的信号和内核对象等 在Linux系统中,有一个经典的概念: …

vue3+threejs新手从零开发卡牌游戏(二十):添加卡牌被破坏进入墓地逻辑

在game目录下新建graveyard文件夹存放墓地相关代码&#xff1a; game/graveyard/p1.vue&#xff0c;这里主要设置了墓地group的位置&#xff1a; <template><div></div> </template><script setup lang"ts"> import { reactive, ref,…

【刷题】 二分查找入门

送给大家一句话: 总有一天&#xff0c;你会站在最亮的地方&#xff0c;活成自己曾经渴望的模样—— 苑子文 & 苑子豪《我们都一样 年轻又彷徨》 二分查找入门 1 前言2 Leetcode 704. 二分查找2.1 题目描述2.2 算法思路 3 Leetcode 34. 在排序数组中查找元素的第一个和最后…

求组合背包II(acwing)

题目描述&#xff1a; 给定n组循问&#xff0c;每组询问给定两个整数a&#xff0c;b&#xff0c;请你输出Ca^b mod (1e9 7)的值&#xff0c;。 输入格式&#xff1a; 第一行包含整数n。 接下来2行&#xff0c;每行包含一组a和b。 输出格式&#xff1a; …

Leetcode 4.1

LeetCode 热题 100 贪心算法1.买卖股票的最佳时机2.跳跃游戏3.跳跃游戏 II4.划分字母区间 区间合并1.合并区间 贪心算法 1.买卖股票的最佳时机 买卖股票的最佳时机 买的那天一定是卖的那天之前的最小值。 每到一天&#xff0c;维护那天之前的最小值即可。 在题目中&#xff0…

面试题:MySQL 优化篇

定位慢查询 &#x1f496; 开源工具 调试工具&#xff1a;Arthas&#xff08;阿尔萨斯&#xff09;运维工具&#xff1a;Prometheus&#xff08;普罗米修斯&#xff09;、Skywalking &#x1f496; MySQL 慢查询日志 # 开启 MySQL 慢查询日志开关 slow_query_log1 # 设置慢…

微信小程序wx.navigateTo无法跳转到Component组件问题解决。(共享元素动画必备)

关于Component构造器官方是有文档说明的&#xff0c;然后官方文档内部也给出了组件是可以通过像pages一样跳转的。但是官方文档缺少了必要的说明&#xff0c;会引起wx.navigateTo无法跳转到组件问题&#xff01; 扫码查看小程序&#xff0c;可以体验效果&#xff1a; 以下是官方…

ElementUI表格table组件实现单选及禁用默认选中效果

在使用ElementUI&#xff0c;需要ElementUI表格table组件实现单选及禁用默认选中效果, 先看下效果图&#xff1a; 代码如下&#xff1a; <template><el-tableref"multipleTable":data"tableData"tooltip-effect"dark"style"widt…

C语言之位段

1.位段的声明 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如&#xff1a; struct A {int _a:2;int _b:5;int _c:10;int _d:30; }; A 就是一个位段类型…