[C语言]利用动态内存制作一个通讯录

news2024/9/25 7:15:52

目录

开辟动态内存的方式

Malloc

free

calloc

realloc

通讯录的制作

源代码

代码解读以及注意事项

开辟动态内存的方式

Malloc

void* malloc (size_t size);

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

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

free

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

void free (void* ptr);

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

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

如果参数ptr是NULL指针,则函数什么事都不做。

malloc和free都声明在 stdlib.h 头文件中。

calloc

C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。原型如下:

void* calloc (size_t num, size_t size);

函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

realloc

realloc函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时 候内存,我们一定会对内存的大小做灵活的调整。那 的调整。

函数原型如下: void* realloc (void* ptr, size_t size);

realloc 函数就可以做到对动态开辟内存大小 ptr 是要调整的内存地址 size 调整之后新大小 返回值为调整之后的内存起始位置。 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。 realloc在调整内存空间的是存在两种情况:

情况1:原有空间之后有足够大的空间比

当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。

情况2:原有空间之后没有足够大的空间

当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。

具体的四个函数如何使用,如果感兴趣的话可以去找找例子,我们这里主要来谈如何利用动态内存来制作一个通讯录,以及使用的时候需要注意的事项

通讯录的制作

源代码

头文件 contact.h

#pragma once
//写一个通讯录,其中人物信息包含姓名、性别、年龄、电话、地址
//需要通讯录具备:增加、删除、搜索、修改、排序、展示、退出
//采用动态内存的分配方式
#define _CRT_SECURE_NO_WARNINGS
#define Max_name 20
#define Max_sex 10
#define Max_phone_num 20
#define Max_addr 30
#define initi_size 3
#define add_size 2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
typedef struct Pepoinfo
{
	char name[Max_name];
	char sex[Max_sex];
	int age;
	char phone_num[Max_phone_num];
	char addr[Max_addr];
}Pepoinfo;
typedef struct Con
{
	Pepoinfo* data;
	int sz;
	int capacity;
}Con;
void initi_Con(Con* addr);
void Add_Con(Con* addr);
void Print_Con(Con* addr);
void Del_Con(Con* addr);
void Search_Con(Con* addr);
void Modify_Con(Con* addr);
void Sort_Con(Con* addr);
void Destory_Con(Con* addr);

contact.c文件

#include "contact.h"
void initi_Con(Con*addr)
{
	//将数据存储在动态内存中
	addr->data=(Pepoinfo*)(malloc(initi_size*sizeof(Pepoinfo)));
	//如果为动态内存不足以分配空间既返回空指针,此时结束初始化
	if (addr->data == NULL)
	{
		perror("initi_Con");
		return;
	}
	addr-> sz = 0;
	addr-> capacity = initi_size;
}
static int is_Con(Con* addr,char*str)
{
	//按名字搜索是否为结构数组内部的元素
	//如是返回数组下标
	//如不是返回-1
	int i;
	for (i = 0; i < addr->sz; i++)
	{
		if (strcmp(addr->data[i].name, str) == 0)
		{
			return i;
		}
	}
	return -1;
}
void Add_Con(Con* addr)
{
	//首先判断是否动态内存需要扩容
	if (addr->sz == addr->capacity)
	{
		Pepoinfo* ret = (Pepoinfo * )(realloc(addr->data, (addr->capacity + add_size)*sizeof(Pepoinfo)));
		if (ret != NULL)
		{
			addr->data = ret;
		}
		else
		{
			perror("Add_Con");
			return;
		}
	}
	//输入数据信息
	printf("请输入需要输入的姓名:\n");
	scanf("%s", addr->data[addr->sz].name);
	printf("请输入需要输入的性别:\n");
	scanf("%s", addr->data[addr->sz].sex);
	printf("请输入需要输入的年龄:\n");
	scanf("%d", &addr->data[addr->sz].age);
	printf("请输入需要输入的手机号:\n");
	scanf("%s", addr->data[addr->sz].phone_num);
	printf("请输入需要输入的地址:\n");
	scanf("%s", addr->data[addr->sz].addr);
	addr->sz++;
}
void Print_Con(Con* addr)
{
	//按照结构体数组的大小下标依次打印数据
	printf("%-20s\t%-10s\t%-s\t%-20s\t%-30s\n", "姓名", "性别", "年龄", "手机号", "地址");
	int i;
	for (i = 0; i < addr->sz; i++)
	{
		printf("%-20s\t%-10s\t%-d\t%-20s\t%-30s\n", addr->data[i].name, 
			                                        addr->data[i].sex, 
	                                         		addr->data[i].age, 
			                                        addr->data[i].phone_num,
                                                    addr->data[i].addr);
	}
}
void Del_Con(Con* addr)
{
	printf("请输入要删除的人:");
	char input_name[Max_name];
	scanf("%s", input_name);
	//is_Con为判断是否为结构体内的数据
	//如是返回下标
	//如不是返回-1
	int ret = is_Con(addr, input_name);
	int i;
	if (ret == -1)
	{
		printf("输入无此人\n");
		return;
	}
	//将结构体的数据往前移既删除了当前下标的值
	for (i = ret; i < addr->sz - 1; i++)
	{
		addr->data[i] = addr->data[i + 1];
	}
	printf("删除成功\n");
	addr->sz--;
}
void Search_Con(Con* addr)
{
	printf("请输入要搜索的人:");
	char input_name[Max_name];
	scanf("%s", input_name);
	int ret = is_Con(addr, input_name);
	if (ret == -1)
	{
		printf("输入无此人\n");
		return;
	}
	printf("%-20s\t%-10s\t%-s\t%-20s\t%-30s\n", "姓名", "性别", "年龄", "手机号", "地址");
	printf("%-20s\t%-10s\t%-d\t%-20s\t%-30s\n", addr->data[ret].name,
		addr->data[ret].sex,
		addr->data[ret].age,
		addr->data[ret].phone_num,
		addr->data[ret].addr);
	return;
}
void Modify_Con(Con* addr)
{
	printf("请输入要修改的人:");
	char input_name[Max_name];
	scanf("%s", input_name);
	int ret = is_Con(addr, input_name);
	if (ret == -1)
	{
		printf("输入无此人\n");
		return;
	}
	printf("请输入需要输入的姓名:\n");
	scanf("%s", addr->data[addr->sz].name);
	printf("请输入需要输入的性别:\n");
	scanf("%s", addr->data[addr->sz].sex);
	printf("请输入需要输入的年龄:\n");
	scanf("%d", &addr->data[addr->sz].age);
	printf("请输入需要输入的手机号:\n");
	scanf("%s", addr->data[addr->sz].phone_num);
	printf("请输入需要输入的地址:\n");
	scanf("%s", addr->data[addr->sz].addr);
	printf("修改成功\n");
}
static int sortbyage(void* struct1, void* struct2)
{
	return((((Pepoinfo*)struct1)->age) - (((Pepoinfo*)struct2)->age));
}
static int sortbyname(void* struct1, void* struct2)
{
	return strcmp((((Pepoinfo*)struct1)->name) , (((Pepoinfo*)struct2)->name));
}
void Sort_Con(Con* addr)
{
	printf("请问需要按照什么进行排序\n");
	printf("****1.年龄  2.姓名******\n");
	//利用qsort进行排序
	int input_sort;
	do
	{
		scanf("%d", &input_sort);
		switch (input_sort)
		{
		case 1:
			qsort(addr->data, addr->sz, sizeof(Pepoinfo), sortbyage);
			break;
		case 2:
			qsort(addr->data, addr->sz, sizeof(Pepoinfo), sortbyname);
			break;
		default:
			printf("输入错误请重新输入:\n");
			break;
		}
	} while (input_sort != 1 && input_sort != 2);
	printf("排序成功\n");
}
void Destory_Con(Con* addr)
{
	//释放内存
	free(addr->data);
	addr->data = NULL;
	addr->sz = 0;
	addr->capacity = 0;
	printf("结束程序\n");

}

主程序Test.c文件

#include "contact.h"
void menu()
{
	printf("************************\n");
	printf("*   1.Add     2.Del    *\n");
	printf("*   3.Search  4.Modify *\n");
	printf("*   5.Sort    6.Print  *\n");
	printf("*       0.Exit         *\n");
	printf("************************\n");
}
void main()
{
	//初始化
	Con Contact;
	initi_Con(&Contact);
	int input; 
	//将需要进行的操作函数放到一个函数指针数组内
	//根据需要的需求排放位置,如输入1为加联系人既吧Add_Con放在下标1处
	void (*option_arr[7])(Con * addr) = { 
		Destory_Con ,
		Add_Con,
		Del_Con,
		Search_Con,
		Modify_Con,
		Sort_Con,
		Print_Con };
	do
	{
		menu();
		printf("请输入需要选择的操作:\n");
		scanf("%d", &input);
		if (0 <= input && input <= 7)
		{
			//根据下标以及输入的参数选择要进行的操作
			option_arr[input](&Contact);
		}
		else
		{
			printf("输入错误请重新输入\n");
		}
	}while (input);
}

代码解读以及注意事项

我们这次的通讯录的任务是

写一个通讯录,其中人物信息包含姓名、性别、年龄、电话、地址
需要通讯录具备:增加、删除、搜索、修改、排序、展示、退出
采用动态内存的分配方式

首先我们要先创建一个人物信息的结构体,以及通讯录的结构体,其中人物信息的补充我们采用分配动态内存空间的方式,如下

其中要注意的是data我们采用动态内存分配,当进行动态内存开辟的时候会返回一个开辟空间的第一个地址的初始指针,我们采用data来进行接受,所以在Contact中初始化data采用Pepoinfo的结构体,但后Con中sz是代表填入了多少个联系人的意思是,capacity是现在data动态内存目前开辟的空间,我们设置初始开辟的空间为3个Peopinfo的大小。

紧接着我们进行第一个关键点的位置就是如何初始化这样的结构体呢?

我们看看我们初始化函数内部的代码

我们来一点点分析这个代码,首先malloc是开辟一个动态内存空间没错,但是要开辟多少呢?我们首先定义初始的内存为三个Pepoinfo的大小,我们定义initi_size的大小为3然后乘上一个sizeof(Pepoinfo)就可以了,我们直到malloc返回的指针类型为空指针,我们需要对其进行类型转换为我们的pepoinfo的结构体,然后再用data进行接收就完成了我们的数据初始化,当然我们要考虑如果不够内存空间分配返回空指针的情况,这个时候我们便结束我们的初始化。

第二个关键点是,对于选择操作,我们建立了一个函数指针数组如下所示

我们定义了销毁(也就是退出)、增加联系人、删除联系人、搜索联系人、修改联系人、排序联系人、以及打印联系人的操作,分别对应了0~6,既输入数字为多少就进行哪个操作。

如下所示

这里我们主要讲讲Add Del 和Sort这三个操作中的重点内容,其他的部分注释的都比较详细

对于增加联系人,我们知道我们动态内存目前之开辟了三个联系人大小的空间,如果我们联系人超出了我们的内存应该怎么样呢?这里我们就可以看看这几行代码

我们先输入一个判断,如果联系人个数sz等于我们的容量了,那我们将realloc我们的空间,返回值我们用结构体指针Pepoinfo修改指针变量为结构体变量,增加大小为我们要设置的add_size的大小,这里我们设置的2就是每次都多增加2个人,我们用ret接收这个指针,接受这个指针是怕传回来的为空,不可以直接用我们之前data指向的地点来接受,不然我们找不到我们原数据的内容。接着如果判断不为空,我们就用原数据data接受这个返回值。这就是Add联系人对于动态指针比较重要的部分

现在我们来看一下Del里面的重点内容

这段代码是找到了我们返回的要删除的数据下标然后进行删除,这里我们发现这段代码的核心思想是下标后的结构体数据往前移覆盖前一个结构体的数据即可完成删除,这里有没有人和我一样有一个疑问,就是结构体里面有char的数组有int类型,我们知道char数组类型如果要进行赋值,得一个个字节的进行赋值,为什么结构体这里可以直接从后往前赋值呢?这样子真的合理吗?

为此我特以写了个程序看看是否可以进行赋值

最终我们看到,结构体是可以直接进行交换的,笔者这里猜想结构体的赋值,应该是地址之间的转换,所以可以直接两个相同结构体赋值即可

第三点就是sort的实现

这里的sort函数我采用了姓名以及年龄两种方式进行排序

其中用到了qsort函数

这里重要的地方就是如果用到qsort对结构体内部某一元素进行整体排序的时候应该如何操作的问题。

和其他的一样前面三个数据分别是,需要排序的地址,需要排序的数组内有几个元素,以及每个原素所占的大小,接着最后一个就是实现这个qsort排序的重点了,我们知道最后一项的返回值是一个int类型的,如果两个数比较,前面比后面的大返回一个大于0的数,后面比前面大返回小于0的数,相等就返回一个等于0的数。那如果我们要按照年龄来排序如何写这个函数呢?我们来看一下下面的代码

因为这里的函数要求我们输入的是void*的指针,我们首先对其转换为Pepoinfo的结构体,然后指向其年龄,然后用第一个减去第二个就可以实现我们的目的了。

用动态内存制作一个通讯录一些重点就在这个地方,其他的地方笔者有注释并且容易看得懂,感谢大家阅读。

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

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

相关文章

34.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-登录数据包的监视与模拟

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;33.游戏登录数据…

100W-200W-300W扁平片式厚膜高压电阻-节省空间的设计

描述 由于其节省空间的设计&#xff0c;EAK采用厚膜技术的 功率电阻器可在狭窄的空间内实现高功率密度。 低电感和提供高电阻值的能力等特性为电力电子开辟了新的前景。 我们的产品组合范围从标准产品到根据您的确切要求进行调整的产品。 告诉我们您想要的输出、尺寸和电阻…

探索 Atlassian 云平台:组织、站点、产品架构解析

我们通常访问的是 Atlassian 的某个云站点&#xff0c;比如填空题-中国站点为&#xff1a;cloze-cn.atlassian.net。当我们访问该站点内的具体产品时&#xff0c;只需在该站点的 URL 后添加相应产品的缩写&#xff0c;例如&#xff1a; Confluence: cloze-cn.atlassian.net/wi…

STM32微控制器中,如何处理多个同时触发的中断请求?

在STM32微控制器中&#xff0c;处理多个同时触发的中断请求需要一个明确的中断优先级策略&#xff0c;以确保关键任务能够及时得到响应。STM32的中断控制器&#xff08;NVIC&#xff09;支持优先级分组&#xff0c;允许开发者为不同的中断设置抢占优先级和子优先级。本文将详细…

【深度学习】pytorch,MNIST手写数字分类

efficientnet_b0的迁移学习 import torch import torch.nn as nn import torch.optim as optim import torchvision.transforms as transforms from torchvision.datasets import MNIST from torch.utils.data import DataLoader from torchvision import models import matplo…

【数据结构】五分钟自测主干知识(十)

上一节&#xff0c;我们讲述了二叉树的概念&#xff0c;二叉树又有什么基本操作呢&#xff1f;今天我们来讲述二叉树的应用~ 话不多说&#xff0c;书继上回 5.3二叉树的遍历及应用 二叉树由三个基本部分组成&#xff1a;根结点&#xff08;D&#xff09;&#xff0c;左子树&a…

ZooKeeper 的常见应用场景

数据发布与订阅 发布与订阅即所谓的配置管理&#xff0c;顾名思义就是将数据发布到ZooKeeper节点上&#xff0c;供订阅者动态获取数据&#xff0c;实现配置信息的集中式管理和动态更新。例如全局的配置信息&#xff0c;地址列表等就非常适合使用。 数据发布/订阅的一个常见的…

Spring Boot:基础配置

Spring Boot 全局配置文件application.propertiesapplication.yml全局配置文件的优先级 从全局配置文件中获取数据的注解从外部属性文件中获取数据的注解全局配置文件的配置项通用配置项数据源配置项JPA 配置项日志配置项配置文件特定配置项Profile 特定配置项 配置类配置文件中…

【Emgu CV教程】10.4、轮廓之多边形近似拟合

文章目录 一、什么叫轮廓的多边形近似拟合二、轮廓的多边形近似拟合函数三、简单应用1.原始素材2.代码3.运行结果 一、什么叫轮廓的多边形近似拟合 轮廓一般都是光滑的曲线&#xff0c;多边形近似拟合的意思就是&#xff0c;利用少量的点组成的折线&#xff0c;近似逼近原始多…

AIGC实战——Transformer模型

AIGC实战——Transformer模型 0. 前言1. T52. GPT-3 和 GPT-43. ChatGPT小结系列链接 0. 前言 我们在 GPT (Generative Pre-trained Transformer) 一节所构建的 GPT 模型是一个解码器 Transformer&#xff0c;它逐字符地生成文本字符串&#xff0c;并使用因果掩码只关注输入字…

力扣98---验证二叉搜索树

题目描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 …

计算联合体union的大小

一&#xff1a;联合类型的定义 联合也是一种特殊的自定义类型&#xff0c;这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09; 比如&#xff1a;共用了 i 这个较大的空间 二&#xff1a; 联合的特点 …

YoloV8改进策略:Block改进|PKINet

摘要 PKINet是面向遥感旋转框的主干,网络包含了CAA、PKI等模块,给我们改进卷积结构的模型带来了很多启发。本文使用PKINet的Block替换YoloV8的Block,实现涨点。改进方法是我独创首发,给写论文没有思路的同学提供改进思路,欢迎大家订阅! 论文:《Poly Kernel Inception …

需求:实现一个类似打印的效果(文字一个字一个字的输出)

实现效果&#xff1a; 需求&#xff1a;最近接到这么一个需求&#xff0c;ai机器人回复的问题&#xff0c;后端是通过websocket每隔一段事件返回数据&#xff0c;前端拿到数据后直接渲染&#xff0c;现在需要做到一个效果&#xff0c;后端返回的结果前端需要一个一个文字的输出…

Unity Canvas的三种模式

一、简介&#xff1a; Canvas的Render Mode一共有三种模式&#xff1a;Screen Space -OverLay、Screen Space-Camera、World Space Screen Space - Overlay&#xff08;屏幕空间 - 覆盖&#xff09;&#xff1a; 这是最简单的 Canvas 渲染模式。UI 元素在这个模式下将渲染在屏…

使用amd架构的计算机部署其他架构的虚拟机(如:arm)

1 下载quem模拟器 https://qemu.weilnetz.de/w64/2 QEMU UEFI固件文件下载(引导文件) 推荐使用&#xff1a;https://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd3 QEMU 安装 安装完成之后&#xff0c;需要将安装目录添加到环境变…

flutter3_douyin:基于flutter3+dart3短视频直播实例|Flutter3.x仿抖音

flutter3-dylive 跨平台仿抖音短视频直播app实战项目。 全新原创基于flutter3.19.2dart3.3.0getx等技术开发仿抖音app实战项目。实现了类似抖音整屏丝滑式上下滑动视频、左右滑动切换页面模块&#xff0c;直播间进场/礼物动效&#xff0c;聊天等模块。 运用技术 编辑器&#x…

C语言字节对齐关键字#pragma pack(n)的使用

0 前言 在进行嵌入式开发的过程中&#xff0c;我们经常会见到对齐操作。这些对齐操作有些是为了便于实现指针操作&#xff0c;有些是为了加速对内存的访问。因此&#xff0c;学习如何使用对齐关键字是对于嵌入式开发是很有必要的。 1 对齐规则 1.0 什么叫做对齐 众所周知&a…

微服务(基础篇-003-Nacos集群搭建)

目录 Nacos集群搭建 1.集群结构图 2.搭建集群 2.1.初始化数据库 2.2.下载nacos 2.3.配置Nacos 2.4.启动 2.5.nginx反向代理 2.6.优化 视频地址&#xff1a; 06-Nacos配置管理-nacos集群搭建_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1LQ4y127n4?p29&…

操作系统究竟是什么?在计算机体系中扮演什么角色?

操作系统究竟是什么&#xff1f;在计算机体系中扮演什么角色&#xff1f; 一、操作系统概念二、操作系统如何管理软硬件资源2.1 何为管理者2.2 操作系统如何管理硬件 三、系统调用接口作用四、用户操作接口五、广义操作系统和狭义操作系统 一、操作系统概念 下面是来自百度百科…