练手项目层中阶2—《详解动态版本——通讯录管理系统》

news2025/1/19 11:35:17

在这里插入图片描述

文章目录

  • 🏳‍🌈前言
    • 🔊项目需求
    • 📝项目知识点包含
    • 🧩项目框架
    • 🔑框架拆解分析
      • 📚Struct_Book1.h头文件分析
      • 📚Struct_Book1.c源文件分析
      • 📚test_book.c源文件分析
    • 🎥效果演示
    • ✨完整代码

🏳‍🌈前言

第一个版本是静态的通讯录,就是用的结构体数组来存储的信息,这样会导致空间不够或者浪费的缺点;因此动态版本就解决这样的问题,减少此情况的发生。接下来讲解中阶版本的练手小项目——《动态通讯录管理系统》

🔊项目需求

动态版本的通讯录也是解决联系人的增、删、查、改、显示、排序的操作。如果看了我写的静态版本的通讯录文章的,其实结构和上一篇文章提到的差不多,只是改变了申请存储空间的方式。通过动态开辟内存来存储信息。

📝项目知识点包含

此动态版本的通讯录,主要是C语言中的动态内存管理知识、结构体、指针操作等,加上C语言的基础知识,接下来就详细描述该项目。👇👇👇👇

🧩项目框架

在这里插入图片描述

🔑框架拆解分析

接下来分别对Struct_Book1.h头文件、Struct_Book1.c源文件、test_book.c源文件作分析:👇

📚Struct_Book1.h头文件分析

在这里插入图片描述
代码展示

#define DEFAULT_SIZE 3

#define MAX_NAME 12
#define MAX_AGE 3
#define MAX_SEX 4
#define MAX_TELE 12
#define MAX_ADDRESS 40
//枚举功能
enum address_book
{
	Exit,
	Add,
	Delete,
	Find,
	Modify,
	Sort,
	Show
};


//信息结构体
typedef struct person_information
{
	char name[MAX_NAME];
	char age[MAX_AGE];
	char sex[MAX_SEX];
	char tel[MAX_TELE];
	char address[MAX_ADDRESS];
}PenInfo;


typedef struct Contact
{
	PenInfo* data;//和静态的对比,就是此处改变了,用结构体指针来表示
	int capacity_size;//动态空间容量
	int number_person;//实际的人的数量
}Con;


void init_contact(Con* ptr);//初始化函数
void addition_information(Con* ptr);//增加函数
void delete_information(Con* ptr);//删除函数
void find_information(Con* ptr);//查找函数
void modify_information(Con* ptr);//修改函数
void sort_information(Con* ptr);//排序函数
void show_information(const Con* ptr);//显示函数
void destroy_contact(Con* ptr);//释放函数

📚Struct_Book1.c源文件分析

直接代码展示出来:主要展示动态申请的代码块

//初始化函数[动态开辟]
void init_contact(Con* ptr)
{
	ptr->data = (PenInfo*)malloc(DEFAULT_SIZE*sizeof(PenInfo));
	//判断
	if (ptr->data == NULL)
	{
		perror("malloc");
		return;
	}
	ptr->capacity_size = DEFAULT_SIZE;//初始的空间大小
	ptr->number_person = 0;
}


//扩容检测
void check_capacity(Con* ps)
{
	assert(ps);
	if (ps->capacity_size == ps->number_person)
	{
		//扩容
		PenInfo* new_capacity = (PenInfo*)realloc(ps->data, 
		(ps->capacity_size+2) * sizeof(PenInfo));//可以修改为成倍数增长
		
		//判断,此处不能直接把开辟的空间,
		//如果开辟失败会返回NULL,那么直接赋值给原来的空间,会把原来的内容全部删除,所以先判断是否申请成功
		if (new_capacity == NULL)
		{
			printf("增容失败!\n");
			return;
		}
		//申请成功
		if (new_capacity != NULL)
		{
			ps->data = new_capacity;
			ps->capacity_size += 2;//此处可以修改成倍数增长,
			//pc->capacity*=2;//此处用倍数,那么在初始化的时候
			//也要改为倍数增长
			printf("增容成功!\n");
			printf("现在容量:%d\n", ps->capacity_size);
		}
	}
}

📚test_book.c源文件分析

代码如下:没有具体说的,比较好理解


void test()
{
	Con con;
	init_contact(&con);
	int input = 0;
	while (1)
	{
		menu();
		printf("请选择功能:\n");
		scanf("%d", &input);
		switch (input)
		{
		case Exit:
			destroy_contact(&con);//主要的是要记住释放申请的空间
			printf("欢迎下次使用!\n");
			return;
		case Add:
			addition_information(&con);
			break;
		case Delete:
			delete_information(&con);
			break;
		case Find:
			find_information(&con);
			break;
		case Modify:
			modify_information(&con);
			break;
		case Sort:
			sort_information(&con);
			break;
		case Show:
			show_information(&con);
			break;
		default:
			printf("选择错误,请重新选择:\n");
			break;
		}
		_getch();
		system("cls");
	}
}

int main()
{
	test();
	return 0;
}

注意:释放申请的动态空间,把它还给计算机内存,“有借有还、再借不难”

🎥效果演示

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

✨完整代码

Struct_Book1.h

#define _CRT_SECURE_NO_WARNINGS 1

#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include<conio.h>
#include<Windows.h>

#define DEFAULT_SIZE 3

#define MAX_NAME 12
#define MAX_AGE 3
#define MAX_SEX 4
#define MAX_TELE 12
#define MAX_ADDRESS 40
//枚举功能
enum address_book
{
	Exit,
	Add,
	Delete,
	Find,
	Modify,
	Sort,
	Show
};


//信息结构体
typedef struct person_information
{
	char name[MAX_NAME];
	char age[MAX_AGE];
	char sex[MAX_SEX];
	char tel[MAX_TELE];
	char address[MAX_ADDRESS];
}PenInfo;


typedef struct Contact
{
	PenInfo* data;
	int capacity_size;
	int number_person;
}Con;


void init_contact(Con* ptr);//初始化函数
void addition_information(Con* ptr);//增加函数
void delete_information(Con* ptr);//删除函数
void find_information(Con* ptr);//查找函数
void modify_information(Con* ptr);//修改函数
void sort_information(Con* ptr);//排序函数
void show_information(const Con* ptr);//显示函数
void destroy_contact(Con* ptr);//释放函数

Struct_Book1.c

#include "Struct_Book1.h"

//初始化函数[动态开辟]
void init_contact(Con* ptr)
{
	ptr->data = (PenInfo*)malloc(DEFAULT_SIZE*sizeof(PenInfo));
	//判断
	if (ptr->data == NULL)
	{
		perror("malloc");
		return;
	}
	ptr->capacity_size = DEFAULT_SIZE;
	ptr->number_person = 0;
}


//扩容检测
void check_capacity(Con* ps)
{
	assert(ps);
	if (ps->capacity_size == ps->number_person)
	{
		//扩容
		PenInfo* new_capacity = (PenInfo*)realloc(ps->data, (ps->capacity_size+2) * sizeof(PenInfo));//可以修改为成倍数增长
		//判断
		if (new_capacity == NULL)
		{
			printf("增容失败!\n");
			return;
		}
		if (new_capacity != NULL)
		{
			ps->data = new_capacity;
			ps->capacity_size += 2;//此处可以修改成倍数增长
			printf("增容成功!\n");
			printf("现在容量:%d\n", ps->capacity_size);
		}
	}
}

//增加函数
void addition_information(Con* ptr)
{
	//增加之前必须判断是否指针为空和空间是否满
	assert(ptr);//断言一下
	//检查是否扩容
	check_capacity(ptr);
	//满足条件,添加数据
	printf("------------------------\n");
	printf("|请输入联系人对应的信息|\n");
	printf("|信息录入中……        |\n");
	printf("------------------------\n");
	printf("姓名—>:");
	scanf("%s", ptr->data[ptr->number_person].name);
	printf("年龄—>:");
	scanf("%s", ptr->data[ptr->number_person].age);
	printf("性别—>:");
	scanf("%s", ptr->data[ptr->number_person].sex);
	printf("电话—>:");
	scanf("%s", ptr->data[ptr->number_person].tel);
	printf("地址—>:");
	scanf("%s", ptr->data[ptr->number_person].address);
	ptr->number_person++;
	printf("添加成功\n");
}


//显示函数
void show_information(const Con* ptr)
{
	assert(ptr);
	if (ptr->number_person == 0)
	{
		printf("通讯录为空,无法显示!\n");
		return;
	}
	printf("*************************************************************************************************\n");
	printf("*****************************          通讯录信息         ***************************************\n");
	printf("*************************************************************************************************\n");
	printf("-------------------------------------------------------------------------------------------------\n");
	printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n","姓名","年龄","性别","电话","地址");
	for (int i = 0; i < ptr->number_person; i++)
	{
		printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n", ptr->data[i].name
			, ptr->data[i].age
			, ptr->data[i].sex
			, ptr->data[i].tel
			,ptr->data[i].address);
	}
	printf("-------------------------------------------------------------------------------------------------\n");



}


//只能在当前.c文件中使用
static int find(Con* ps,char* name1)
{
	assert(ps);
	for (int i = 0; i < ps->number_person; i++)
	{
		//此处不能这样比较
		//if (ps->data[i].name == name1)
		//{
		//	return i;//返回找到的位置
		//}
		if ((strcmp(ps->data[i].name, name1)) == 0)
		{
			return i;
		}
	}
	return -1;
}

//删除函数
void delete_information(Con* ptr)
{
	//断言
	assert(ptr);
	if (ptr->number_person == 0)
	{
		printf("通讯录为空,无法删除!\n");
		return;
	}
	//找到对应要删除的联系人
	char name[MAX_NAME];
	printf("请输入要删除联系人的姓名:");
	scanf("%s", name);
	int ret=find(ptr,name);
	if (ret == -1)
	{
		printf("没找到,无法删除!\n");
		return;
	}
	//删除
	for (int i = ret; i < ptr->number_person - 1; i++)
	{
		ptr->data[i] = ptr->data[i + 1];
	}
	//人数-1
	ptr->number_person--;
	printf("删除成功!\n");
	show_information(ptr);
}


//查找函数
void find_information(Con* ptr)
{
	assert(ptr);
	if (ptr->number_person == 0)
	{
		printf("通讯录为空,无法查找!\n");
		return;
	}
	char name1[MAX_NAME];
	printf("请输入要查找联系人的姓名:");
	scanf("%s", name1);
	int ret = find(ptr,name1);
	if (ret == -1)
	{
		printf("没找到!\n");
		return;
	}
	printf("-------------------------------------------------------------------------------------------------\n");
	printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("|%-12s\t%-3s\t%-4s\t%-12s\t\t%-40s|\n", ptr->data[ret].name
			, ptr->data[ret].age
			, ptr->data[ret].sex
			, ptr->data[ret].tel
			, ptr->data[ret].address);
	printf("-------------------------------------------------------------------------------------------------\n");
}


//修改函数
void modify_information(Con* ptr)
{
	assert(ptr);
	if (ptr->number_person == 0)
	{
		printf("通讯录为空,无法修改!\n");
		return;
	}
	char name2[MAX_NAME];
	printf("请输入要修改联系人的姓名:");
	scanf("%s", name2);
	int ret = 0;
	ret = find(ptr,name2);
	if (ret == -1)
	{
		printf("没找到!\n");
		return;
	}
	printf("修改进行中…………\n");
	printf("请输入新的姓名:");
	scanf("%s", ptr->data[ret].name);
	printf("请输入新的年龄:");
	scanf("%s", ptr->data[ret].age);
	printf("请输入新的性别:");
	scanf("%s", ptr->data[ret].sex);
	printf("请输入新的电话:");
	scanf("%s", ptr->data[ret].tel);
	printf("请输入新的地址:");
	scanf("%s", ptr->data[ret].address);

	printf("修改成功!\n");
	show_information(ptr);
}



 int compar_by_name(const void* e1, const void* e2)
 {
	 return strcmp(((PenInfo*)e1)->name,((PenInfo*)e2)->name);
 }

 int compar_by_age(const void* e1, const void* e2)
 {
	 return strcmp(((PenInfo*)e1)->age, ((PenInfo*)e2)->age);
 }


//排序函数
void sort_information(Con* ptr)
{
	assert(ptr);
	if (ptr->number_person == 0)
	{
		printf("通讯录为空,无法排序!\n");
		return;
	}

	printf("请选择排序方式:1、按照姓名排序,2、按照年龄排序。\n");
	int select1= 0;
	scanf("%d", &select1);
	if (select1 == 1)
	{
		//qsort()用法
		//void qsort (void* base, size_t num, size_t size,int (*compar)(const void*, const void*));
		/*
		base ——>Pointer to the first object of the array to be sorted, converted to a void*.
		num  ——>Number of elements in the array pointed to by base.size_t is an unsigned integral type.
		size  ——> Size in bytes of each element in the array.size_t is an unsigned integral type.
		compar  ——> Pointer to a function that compares two elements.This function is called repeatedly 
		by qsort to compare two elements. 
		It shall follow the following prototype:int compar (const void* p1, const void* p2);*/

		qsort(ptr->data, ptr->number_person, sizeof(PenInfo), compar_by_name);
		printf("排序成功!\n");
	}
	if (select1 == 2)
	{
		qsort(ptr->data, ptr->number_person, sizeof(PenInfo), compar_by_age);
		printf("排序成功!\n");
	}

	
}


//释放函数
void destroy_contact(Con* ptr)
{
	free(ptr->data);
	ptr->data = NULL;
}

test_book.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Struct_Book1.h"


void menu()
{
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t*********        通讯录       *********\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  1、添加联系人  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  2、删除联系人  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  3、查找联系人  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  4、修改联系人  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  5、通讯录排序  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  6、显示联系人  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");
	printf("\t\t\t\t\t****  ——>  0、退出通讯录  <——  ****\n");
	printf("\t\t\t\t\t***************************************\n");

}


void test()
{
	Con con;
	init_contact(&con);
	int input = 0;
	while (1)
	{
		menu();
		printf("请选择功能:\n");
		scanf("%d", &input);
		switch (input)
		{
		case Exit:
			destroy_contact(&con);
			printf("欢迎下次使用!\n");
			return;
		case Add:
			addition_information(&con);
			break;
		case Delete:
			delete_information(&con);
			break;
		case Find:
			find_information(&con);
			break;
		case Modify:
			modify_information(&con);
			break;
		case Sort:
			sort_information(&con);
			break;
		case Show:
			show_information(&con);
			break;
		default:
			printf("选择错误,请重新选择:\n");
			break;
		}
		_getch();
		system("cls");
	}
}

int main()
{
	test();
	return 0;
}

动态版本的就这样了,后面会更新文件版本和数据库版本的通讯录👌
在这里插入图片描述

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

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

相关文章

MySql并发事务问题

事务 事务概念&#xff1a; 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务的特性&#xff1a;ACID&#xff1a; 小…

【Leetcode每日一题】模拟 - 外观数列(难度⭐⭐)(51)

1. 题目解析 题目链接&#xff1a;38. 外观数列 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 所谓“外观数列”&#xff0c;其实只是依次统计字符串中连续且相同的字符的个数。依照题意&#xff0c;依次模拟即 可。…

java小项目(2)--求数组的和,平均数,大于平均数的值(第二遍)

题目&#xff1a; 代码&#xff1a; import java.util.Scanner; public class Nain{public static void main(String[] args){Scanner hhh new Scanner(System.in); //输入前的准备int n hhh.nextInt(); //输入nint[] value new int[n]; //创建数组并定义数组的大小S…

【yolov8】修改保存设置,SETTINGS

可以手动修改这个地方&#xff0c;也可以用代码来控制 from ultralytics.utils import SETTINGS print(SETTINGS["runs_dir"]) print(SETTINGS) update_params {runs_dir: runs} SETTINGS.update(update_params) print(SETTINGS)

【数学推理】蓝桥杯第十四届---阶乘的和

题目描述 给定 n 个数 &#xff0c;问能满足 m! 为 的因数的最大的 m 是多少。其中 m! 表示 m 的阶乘&#xff0c;即 1 2 3 m。 输入格式 输入的第一行包含一个整数 n 。 第二行包含 n 个整数&#xff0c;分别表示 Ai&#xff0c;相邻整数之间使用一个空格分隔。 输…

文献学习-28-Endora: 用于内镜仿真的视频生成模型

Endora : Video Generation Models as Endoscopy Simulators Authors: Chenxin Li, Hengyu Liu, Yifan Liu, Brandon Y. Feng, Wuyang Li, Xinyu Liu, Zhen Chen, Jing Shao, Yixuan Yuan Keywords: Medical Generative AI Video Generation Endoscopy Abstract 生成模型有…

Win安装SSH教程

在Windows操作系统上安装和配置SSH&#xff08;Secure Shell&#xff09;可以让你通过加密的方式远程连接和管理其他计算机或服务器。以下是安装和配置SSH的简单教程&#xff1a; 下载OpenSSH for Windows&#xff1a; 访问OpenSSH for Windows的官方网站&#xff08;https://g…

云备份day03

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C云备份项目 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 主要内容介绍了第三方库httplib的一些内容&#xff0c;以及实现…

「 典型安全漏洞系列 」10.跨域资源共享CORS漏洞详解

跨域资源共享&#xff08;Cross-origin Resource Sharing&#xff0c;CORS&#xff09;是一种浏览器机制&#xff0c;可以对于给定域之外的资源进行受控访问。它扩展并增加了同源政策&#xff08;Same-origin Policy&#xff0c;SOP&#xff09;的灵活性。然而&#xff0c;如果…

蓝鲸6.1 CMDB 事件推送的开源替代方案

本文来自腾讯蓝鲸智云社区用户&#xff1a;木讷大叔爱运维 背景 在蓝鲸社区“社区问答”帖子中发现这么一个需求&#xff1a; 究其原因&#xff0c;我在《不是CMDB筑高墙&#xff0c;运维需要一定的开发能力&#xff01;》一文中已经介绍&#xff0c;在此我再简单重复下&#…

吴恩达机器学习笔记:第 6 周-11机器学习系统的设计(Machine Learning System Design)11.1-11.5

目录 第 6 周 11、 机器学习系统的设计(Machine Learning System Design)11.1 首先要做什么11.2 误差分析11.3 类偏斜的误差度量11.4 查准率和查全率之间的权衡11.5 机器学习的数据 第 6 周 11、 机器学习系统的设计(Machine Learning System Design) 11.1 首先要做什么 在接…

Docker简单介绍、特点、与虚拟机技术的区别、核心概念及在CentOS 7 中安装卸载Docker

目录 一、什么是Docker 二、特点 三、Docker与虚拟机技术的区别 四、Docker的核心概念 Docker仓库与仓库注册服务器的区别 五、CentOS7在线安装Docker 安装配置 卸载 一、什么是Docker Docker是一个开源的容器化平台&#xff0c;用于打包、部署和运行应用程序。它利用…

移动端适配方案总结之vw

1、vw/vh是什么&#xff1f; vw是&#xff1a;viewport width 视口宽度单位 vh是&#xff1a; viewport height 视口高度单位 实际开发中我们基本用vw&#xff1b; 2.相对视口的尺寸计算结果 1vw 1/100视口宽度 1vh 1/100视口高度 例如&#xff1a; 当前屏幕视口是 375像素…

聚类算法的先验基础知识

聚类算法的先验基础知识 1. 瑞利商2. 谱定理3. 联合概率4. 条件概率分布5. 边缘分布6. 贝叶斯定理7. 有向图8. 拉格朗日乘子定理 下一篇将介绍整理各种聚类算法&#xff0c;包括k-means&#xff0c;GMM(Guassian Mixture Models, 高斯混合)&#xff0c;EM(Expectation Maximiza…

如果真的要被优化了,你该如何提前应对与准备!

程序员35岁会失业吗&#xff1f; 35岁被认为是程序员职业生涯的分水岭&#xff0c;许多程序员开始担忧自己的职业发展是否会受到年龄的限制。随着科技的飞速发展&#xff0c;IT行业成为了变化最为迅速的领域之一。在这样的背景下&#xff0c;许多35岁左右的程序员开始担忧自己…

Android数据存储技术

一、文件存储 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:orientation"vertical"android:layout_width"match_parent"android:layout_height"match_parent" ><EditTextandroid:id&qu…

【C++】c++11新特性(一)

目录 { }列表初始化 内置类型---对单值变量及数组的初始化 列表初始化时进行的类型转换 自定义类型---对类对象或结构的初始化 initializer_list 1. 定义接受 initializer_list 参数的构造函数 2. 在函数中使用 initializer_list 参数 3. 使用 initializer_list 与 vect…

C#将Console写至文件,且文件固定最大长度

参考文章 将C#的Console.Write同步到控制台和log文件输出 业务需求 在生产环境中&#xff0c;控制台窗口不便展示出来。 为了在生产环境中&#xff0c;完整记录控制台应用的输出&#xff0c;选择将其输出到文件中。 但是&#xff0c;一次性存储所有输出的话&#xff0c;文件会…

基于SSM+Jsp+Mysql的二手车交易网站

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

5G智慧地铁数字孪生可视化平台,推进铁路行业数字化转型

随着科技的快速发展&#xff0c;5G智慧地铁数字孪生可视化平台正逐渐成为铁路行业数字化转型的重要推动力。巨蟹数科数字孪生平台集成了5G通信技术、大数据分析、云计算和人工智能等先进技术&#xff0c;通过构建数字孪生模型&#xff0c;实现对地铁运营全过程的实时监控、预测…