【C语言】第四十二弹---一万六千字教你从0到1实现通讯录

news2024/11/15 17:04:55

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】

目录

1、通讯录分析和设计

1.1、通讯录的功能说明

1.2、程序的分析和设计

1.2.1、数据结构的分析

1.2.2、文件结构设计

2、通讯录的结构分析

2.1、创建通讯录结构

2.1.1、静态版本

2.1.2、动态版本

2.2、用户选择

2.3、初始化通讯录

2.3.1、静态通讯录

2.3.2、动态通讯录

2.3.3、动态文件通讯录

2.4、添加联系人信息

2.5、打印联系人信息

2.6、删除联系人

2.7、 查找联系人

2.8、修改联系人

2.9、排序联系人信息

2.10、存储联系人信息到文件

2.11、销毁通讯录

3、完整代码

3.1、contact.h

3.2、contact.c 

3.3、test.c

总结


前面接近四十弹内容基本都在学习C语言的基本语法,单纯的学习语法会让我们的学习比较枯燥,因此该节内容通过C语言学习的语法知识实现一个我们手机都有的通讯录功能。

1、通讯录分析和设计


1.1、通讯录的功能说明

1、通讯录中包含个人的姓名,性别,年龄,电话与地址。

2、用户可以自由进出程序。

3、用户可以自由增删查改以及排序通讯录中的数据。

程序的界面:

1.2、程序的分析和设计


1.2.1、数据结构的分析

通讯录中存储的数据有姓名,性别,年龄,电话与地址,此处会用到字符串(字符串通过字符数组进行实现),整型的结合,因此我们可以封装成一个结构体进行统一存储。根据通讯录功能的说明,我们需要对通讯录进行增删改查操作,如何才能确定需要操作的数据是哪一组呢?如何确定最终存储了多少组数据呢?

解决办法如下:

1、再创建一个结构体,该结构体的内容包括存储数据的结构体和该结构体的最大存储个数(静态版本,创建静态数组)。

2、再创建一个结构体,该结构体的内容包括存储数据的结构体、存储数据的结构体的实际存储个数以及存储数据的结构体的(容量)最大存储个数(动态版本,动态开辟数组)。

知道数据的存储之后就是创建我们常用的菜单,即前面扫雷,猜数字游戏用到的菜单,通讯录也是同样的原理,用do while循环实现此菜单,根据上面程序的界面可以知道,我们需要实现接口函数较多,因此推荐使用switch选择语句,但是switch语句的类型需要整型,单纯我们整数去实现该函数不够通俗易懂,此处的解决办法有1、通过#define定义常量 2、使用枚举类型

如果使用#define定义常量,需要7处,代码也比较繁琐,但是如果此处使用枚举类型,可以定义一次即可(枚举类型不初始化第一个变量自动初始化为0,后面依次加+),非常满足此处的需求

后面就是基本接口函数的实现了,在后面一一详细讲解。


1.2.2、文件结构设计


之前学习了多文件的形式对函数的声明和定义,这里我们实践⼀下,我们设计三个⽂件:

test.c : 文件中写程序的测试逻辑

Contact.c : 文件中写程序中函数的实现等

Contact.h : 件中写程序需要的数据类型和函数声明等

建议:写一些代码就测试一些代码。

2、通讯录的结构分析

2.1、创建通讯录结构

2.1.1、静态版本

静态通讯录就是在开辟空间时,给一个固定的大小缺陷在于空间小了,不能扩容,空间大了,会造成浪费。

//常量使用#define宏定义,便于以后修改

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 13
#define ADDR_MAX 30

#define MAX 100  //通讯录大小,存放多少人的信息
//一个人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

//静态版本,通讯录的信息
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz;
}Contact;

2.1.2、动态版本

动态通讯录就是对静态通讯录的缺陷进行改进通过动态内存管理来开辟空间,减少空间浪费的问题。

//常量使用#define宏定义,便于以后修改

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 13
#define ADDR_MAX 30

//一个人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;
}Contact;

2.2、用户选择

首先用户选择需要的功能,输入1则添加信息,输入2则删除信息,输入3则查询信息,输入4则修改信息,输入5则展示信息,输入6则排序信息,输入0则退出游戏输入其他值则重新输入。

从这可以知道此处为一个循环,而且一定会进入一次,符号do while的特性,因此使用do while循环,但是此处需要打印一个选项的界面,因此可以使用创建一个菜单。

为了更好的表示switch中每个分支的意思,此处使用枚举变量来表示!!! 

主体函数如下:

该结构中是包含文件版本的,在退出程序之前会保存联系人信息。

void menu()
{
	printf("********************************************\n");
	printf("**********   1.add      2.del     **********\n");
	printf("**********   3.search   4.modify  **********\n");
	printf("**********   5.show     6.sort    **********\n");
	printf("**********   0.exit               **********\n");
	printf("********************************************\n");
}

//增强代码可读性,使用枚举变量
enum Option
{
	EXIT,//退出游戏,默认值为0
	ADD,//添加联系人信息
	DEL,//删除联系人信息
	SEARCH,//查找联系人信息
	MODIFY,//修改联系人信息
	SHOW,//打印联系人信息
	SORT//排序联系人信息
};

int main()
{
	int input = 0;
	Contact con;//创建通讯录,不初始化里面的值是随机值,我们需要将大小初始化为0
	//初始化通讯录
	InitContact(&con);//形参不能修改实参,需要传地址
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			SaveContact(&con);//保存联系人信息
			DestroyContact(&con);//销毁通讯录
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

2.3、初始化通讯录

通讯录初始化有三个版本,一个是静态数组的版本,一个是动态数组的版本,一个是动态数组加文件的版本,因此操作可能有些许的差异,但是总体逻辑基本一致。

2.3.1、静态通讯录

使用内存设置函数将通讯录的内容都设置为0。

//静态版本
void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	memset(pc->data, 0, sizeof(pc->data));//将整个存放信息的数组初始化为0
}

2.3.2、动态通讯录

先开辟3个空间,将大小初始化为0,容量初始化为3。

#define DEFAULT_SZ 3//初始化空间大小
//动态版本
void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	pc->capacity = DEFAULT_SZ;
	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact malloc");
		return;
	}
}

2.3.3、动态文件通讯录

动态文件通讯录就是对动态通讯录不能保存数据进行优化,在使用通讯录之前先从文件中导入数据,再进行通讯录操作。

注意:文件版本因为需要导入数据,可能出现容量不够的情况,因此需要判断容量,因此需要在前面实现检查容量的函数。

检查容量函数:

#define DEFAULT_INC 2 //一次增减两个容量
void Check_Capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += DEFAULT_INC;
			printf("增容成功\n");
		}
		else
		{
			perror("AddContact->realloc");
			return;
		}
	}
}

初始化函数: 

使用fread读取文件信息一次读取一个,如果返回值为0,说明文件信息读取完毕,退出循环即可。

void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	pc->capacity = DEFAULT_SZ;
	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact malloc");
		return;
	}
	//读取文件
	FILE* pf = fopen("contact.txt", "r");
	if (pf == NULL)
	{
		perror("InitContact");
		return;
	}
	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))//每次读取一个大小,返回值为0则结束读取
	{
		Check_Capacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
}

2.4、添加联系人信息

添加联系人信息输入该联系人的相关信息将信息存放到通讯录中,但是在添加信息之前需要检查容量是否充足。 

void AddContact(Contact* pc)
{
	assert(pc);
	printf("添加联系人信息\n");
	//容量满了需要增容
	Check_Capacity(pc);
	printf("请输入名字:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("增加成功\n");
}

2.5、打印联系人信息

为了测试添加联系人信息是否添加成功,因此先实现一个打印信息的函数,也为后面测试做铺垫。

为了打印出来的信息更加美观,博主在打印之前按照占位符对其数据进行打印,uu们也可以按照自己的喜好设置。

遍历整个通讯录,依次打印通讯录中的信息。

void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("打印联系人信息\n");
	//判断是否有联系人
	if (pc->sz == 0)
	{
		printf("无联系人,无需打印\n");
		return;
	}
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-15s%-5d%-5s%-13s%-20s\n",
			pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}

2.6、删除联系人

博主实现的是通过名字搜索来查找删除的联系人,uu们可以按照自己的需要设计其他的查找函数喔,因为查找函数只需要在contact.c文件中使用,因此博主将查找函数加了static修饰,只能在该文件使用此函数。

按照名字查找函数:

找到则返回下标,没找到返回-1.

static int FindByName(const Contact* pc, char name[])//static修饰,只在该文件中使用
{
	int i = 0;
	for (i = 0; i < pc->sz; i++) 
	{
		if ((strcmp(pc->data[i].name, name)) == 0)
		{
			return i;
		}
	}
	return -1;//没有找到
}

删除联系人函数 

删除联系人信息之前判断通讯录中是否有数据,如果没有数据则给出提示,如果有数据再进行查找删除操作。

void DelContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("删除联系人信息\n");
	if (pc->sz == 0)
	{
		printf("无联系人,无需删除\n");
		return;
	}
	//删除谁的信息,使用名字查找
	printf("请输入要删除联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人,无需删除\n");
		return;
	}
	//删除信息 把后面一个人的信息赋值给前面一个人
	int i = 0;
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

2.7、 查找联系人

通过输入名字查找联系人没有找到则给出提示找到则打印该联系人的相关信息。(uu们也可以设计其他的查找方式喔!!!)

void SearchContact(const Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("查找联系人信息\n");
	printf("请输入要查找联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//打印该联系人信息
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	printf("%-15s%-5d%-5s%-13s%-20s\n",
		pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}

2.8、修改联系人

通过输入名字查找联系人没有找到则给出提示找到则修改该联系人的相关信息

void ModifyContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("修改联系人信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	printf("请输入名字:\n");
	scanf("%s", pc->data[ret].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[ret].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[ret].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[ret].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[ret].addr);

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

2.9、排序联系人信息

排序方式函数:

此处实现的函数均为升序函数,包含所以成员变量的升序。

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_by_age(const void* e1, const void* e2)
{
	return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_by_sex(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex);
}
int cmp_by_tele(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
int cmp_by_addr(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr);
}

void SortByName(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}
void SortByAge(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_age);
}
void SortBySex(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_sex);
}
void SortByTele(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_tele);
}
void SortByAddr(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_addr);
}

排序联系人函数

输入想要进行排序的方式,输入错误会提示重新输入。

void SortContact(Contact* pc)
{
	assert(pc);
	char input[20];
	printf("排序联系人信息\n");
	printf("请选择如何进行排序,选择有名字,年龄,性别,电话,地址!\n");
	while (1)
	{
		scanf("%s", input);
		if (strcmp(input, "名字") == 0)
		{
			printf("按照姓名升序\n");
			SortByName(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "年龄") == 0)
		{
			printf("按照年龄升序\n");
			SortByAge(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "性别") == 0)
		{
			printf("按照性别升序\n");
			SortBySex(pc);
			ShowContact(pc);
		}
		else if (strcmp(input, "电话") == 0)
		{
			printf("按照电话升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "地址") == 0)
		{
			printf("按照地址升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else
		{
			printf("输入错误,请重新选择\n");
		}
	}
}

 2.10、存储联系人信息到文件

数据写入文件函数

使用fwrite函数依次将数据写入到文件中。

void SaveContact(Contact* pc)
{
	//将通讯录信息写入到文件中
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//一个信息一个信息写入
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
}

2.11、销毁通讯录

动态版本通讯录才需要销毁通讯录。

释放动态开辟的内存空间,并将大小个容量初始化为0.

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

3、完整代码

3.1、contact.h

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

//常量使用#define宏定义,便于以后修改
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 13
#define ADDR_MAX 30

#define MAX 100  //通讯录大小,存放多少人的信息

#define DEFAULT_SZ 3
#define DEFAULT_INC 2


typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

//静态版本
//typedef struct Contact
//{
//	PeoInfo data[MAX];
//	int sz;
//}Contact;


typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;
}Contact;


//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//显示所有联系人信息
void ShowContact(const Contact* pc);//不会修改值,可以加const
//删除指定联系人
void DelContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//排序通讯录
void SortContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);
//将通讯录信息写入到文件中
void SaveContact(Contact* pc);

3.2、contact.c 

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

//静态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);//需要对数据进行解引用,加assert断言
//	pc->sz = 0;//将联系人个数初始化为0
//	memset(pc->data, 0, sizeof(pc->data));//将整个存放信息的数组初始化为0
//}

//动态版本
//void InitContact(Contact* pc)
//{
//	assert(pc);//需要对数据进行解引用,加assert断言
//	pc->sz = 0;//将联系人个数初始化为0
//	pc->capacity = DEFAULT_SZ;
//	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
//	if (pc->data == NULL)
//	{
//		perror("InitContact malloc");
//		return;
//	}
//}

void Check_Capacity(Contact* pc);

//文件版本
void InitContact(Contact* pc)
{
	assert(pc);//需要对数据进行解引用,加assert断言
	pc->sz = 0;//将联系人个数初始化为0
	pc->capacity = DEFAULT_SZ;
	pc->data = (PeoInfo*)calloc(pc->capacity, sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact malloc");
		return;
	}
	//读取文件
	FILE* pf = fopen("contact.txt", "r");
	if (pf == NULL)
	{
		perror("InitContact");
		return;
	}
	PeoInfo tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))//每次读取一个大小,返回值为0则结束读取
	{
		Check_Capacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
}

void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}


//静态版本
//void AddContact(Contact* pc)
//{
//	assert(pc);
//	//判断通讯录是否满了
//	if (pc->sz == MAX)
//	{
//		printf("通讯录已满,不能添加联系人\n");
//		return;
//	}
//	printf("请输入名字:\n");
//	scanf("%s", pc->data[pc->sz].name);
//	printf("请输入年龄:\n");
//	scanf("%d", &(pc->data[pc->sz].age));
//	printf("请输入性别:\n");
//	scanf("%s", pc->data[pc->sz].sex);
//	printf("请输入电话:\n");
//	scanf("%s", pc->data[pc->sz].tele);
//	printf("请输入地址:\n");
//	scanf("%s", pc->data[pc->sz].addr);
//
//	pc->sz++;
//	printf("增加成功\n");
//}

void Check_Capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += DEFAULT_INC;
			printf("增容成功\n");
		}
		else
		{
			perror("AddContact->realloc");
			return;
		}
	}
}
void AddContact(Contact* pc)
{
	assert(pc);
	printf("添加联系人信息\n");
	//容量满了需要增容
	Check_Capacity(pc);
	printf("请输入名字:\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("增加成功\n");
}

void ShowContact(const Contact* pc)
{
	assert(pc);
	printf("打印联系人信息\n");
	//判断是否有联系人
	if (pc->sz == 0)
	{
		printf("无联系人,无需打印\n");
		return;
	}
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-15s%-5d%-5s%-13s%-20s\n",
			pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}

static int FindByName(const Contact* pc, char name[])//static修饰,只在该文件中使用
{
	int i = 0;
	for (i = 0; i < pc->sz; i++) 
	{
		if ((strcmp(pc->data[i].name, name)) == 0)
		{
			return i;
		}
	}
	return -1;//没有找到
}
void DelContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("删除联系人信息\n");
	if (pc->sz == 0)
	{
		printf("无联系人,无需删除\n");
		return;
	}
	//删除谁的信息,使用名字查找
	printf("请输入要删除联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人,无需删除\n");
		return;
	}
	//删除信息 把后面一个人的信息赋值给前面一个人
	int i = 0;
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}


void SearchContact(const Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("查找联系人信息\n");
	printf("请输入要查找联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//打印该联系人信息
	printf("%-15s%-5s%-5s%-13s%-20s\n", "名字", "年龄", "性别", "电话", "地址");
	printf("%-15s%-5d%-5s%-13s%-20s\n",
		pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}

void ModifyContact(Contact* pc)
{
	char name[NAME_MAX];
	assert(pc);
	printf("修改联系人信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	printf("请输入名字:\n");
	scanf("%s", pc->data[ret].name);
	printf("请输入年龄:\n");
	scanf("%d", &(pc->data[ret].age));
	printf("请输入性别:\n");
	scanf("%s", pc->data[ret].sex);
	printf("请输入电话:\n");
	scanf("%s", pc->data[ret].tele);
	printf("请输入地址:\n");
	scanf("%s", pc->data[ret].addr);

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

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_by_age(const void* e1, const void* e2)
{
	return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_by_sex(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex);
}
int cmp_by_tele(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
int cmp_by_addr(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr);
}

void SortByName(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}
void SortByAge(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_age);
}
void SortBySex(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_sex);
}
void SortByTele(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_tele);
}
void SortByAddr(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_addr);
}

void SortContact(Contact* pc)
{
	assert(pc);
	char input[20];
	printf("排序联系人信息\n");
	printf("请选择如何进行排序,选择有名字,年龄,性别,电话,地址!\n");
	while (1)
	{
		scanf("%s", input);
		if (strcmp(input, "名字") == 0)
		{
			printf("按照姓名升序\n");
			SortByName(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "年龄") == 0)
		{
			printf("按照年龄升序\n");
			SortByAge(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "性别") == 0)
		{
			printf("按照性别升序\n");
			SortBySex(pc);
			ShowContact(pc);
		}
		else if (strcmp(input, "电话") == 0)
		{
			printf("按照电话升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else if (strcmp(input, "地址") == 0)
		{
			printf("按照地址升序\n");
			SortByTele(pc);
			ShowContact(pc);
			break;
		}
		else
		{
			printf("输入错误,请重新选择\n");
		}
	}
}

void SaveContact(Contact* pc)
{
	//将通讯录信息写入到文件中
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}
	//一个信息一个信息写入
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	fclose(pf);
	pf = NULL;
}

3.3、test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"
void menu()
{
	printf("********************************************\n");
	printf("**********   1.add      2.del     **********\n");
	printf("**********   3.search   4.modify  **********\n");
	printf("**********   5.show     6.sort    **********\n");
	printf("**********   0.exit               **********\n");
	printf("********************************************\n");
}

//增强代码可读性,使用枚举变量
enum Option
{
	EXIT,//退出游戏,默认值为0
	ADD,//添加联系人信息
	DEL,//删除联系人信息
	SEARCH,//查找联系人信息
	MODIFY,//修改联系人信息
	SHOW,//打印联系人信息
	SORT//排序联系人信息
};

int main()
{
	int input = 0;
	Contact con;//创建通讯录,不初始化里面的值是随机值,我们需要将大小初始化为0
	//初始化通讯录
	InitContact(&con);//形参不能修改实参,需要传地址
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

mysql5.6的安装步骤

1.下载mysql 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 在这里我们下载zip的包 2.解压mysql包到指定目录 3. 添加my.ini文件 # For advice on how to change settings please see # http://dev.mysql.com/doc/refman/5.6/en/server-configurat…

docker-compose Install gitlab 17.1.1

gitlab 前言 GitLab 是一个非常流行的开源 DevOps 平台,用于软件开发项目的整个生命周期管理。它提供了从版本控制、持续集成/持续部署(CI/CD)、项目规划到监控和安全的一系列工具。 前提要求 Linux安装 docker docker-compose 参考Windows 10 ,11 2022 docker docker-c…

11.x86游戏实战-汇编指令add sub inc dec

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;10.x86游戏实战-汇编指令lea 首先双击下图红框位置 然后在下图红框位置输入0 然…

瞰景Smart3D使用体验分享

引言 作为一名建筑设计师&#xff0c;我一直在寻找能够提升工作效率和设计质量的软件工具。瞰景Smart3D&#xff08;Smart3D&#xff09;是一款备受推崇的3D建模和设计软件&#xff0c;广泛应用于建筑、工程和施工&#xff08;AEC&#xff09;行业。经过一段时间的使用&#x…

MySQL表的增删改查(CRUD)

MySQL表的增删改查&#xff08;CRUD&#xff09; 文章目录 MySQL表的增删改查&#xff08;CRUD&#xff09;1. Create1.1 单行数据 全列插入1.2 多行数据 指定列插入1.3 插入否则更新1.4 替换 2. Retrieve2.1 SELECT 列2.1.1 全列查询2.1.2 指定列查询2.1.3 查询字段为表达式…

荞面打造的甜蜜魔法:甜甜圈

食家巷荞面甜甜圈是一款具有特色的美食。它以荞面为主要原料&#xff0c;相较于普通面粉&#xff0c;荞面具有更高的营养价值&#xff0c;富含膳食纤维、维生素和矿物质。荞面甜甜圈的口感可能会更加扎实和有嚼劲&#xff0c;同时带着荞面特有的谷物香气。在制作过程中&#xf…

一款纯 js 实现的大模型应用服务 FastGPT 解读

背景介绍 最近被不同的人安利了 FastGPT 项目&#xff0c;实际上手体验了一下&#xff0c;使用流程类似之前调研过的 Dify, 包含的功能主要是&#xff1a;任务流的编排&#xff0c;知识库管理&#xff0c;另外还有一些外部工具的调用能力。使用页面如下所示&#xff1a; 实际…

游戏服务器搭建选VPS还是专用服务器?

游戏服务器搭建选VPS&#xff0c;VPS能够提供控制、性能和稳定性。它不仅仅是让游戏保持活力。它有助于减少延迟问题&#xff0c;增强您的游戏体验。 想象一下&#xff1a;你正沉浸在一场游戏中。 胜利在望。突然&#xff0c;屏幕卡住——服务器延迟。 很崩溃&#xff0c;对…

Drools开源业务规则引擎(四)- 规则流(rule flow)及手把手教你构建jBPM项目

文章目录 Drools开源业务规则引擎&#xff08;四&#xff09;- 规则流&#xff08;rule flow&#xff09;及手把手教你构建jBPM项目1.什么是规则流2.构建jBPM项目2.1.添加maven依赖2.2.创建kmodule.xml2.3.新建drl文件和bpmn2文件2.4.测试方法2.5.日志输出 3.Eclipse安装jBPM流…

24西安电子科技大学马克思主义学院—考研录取情况

01、马克思主义学院各个方向 02、24马克思主义学院近三年复试分数线对比 PS&#xff1a;马院24年院线相对于23年院线增加15分&#xff0c;反映了大家对于马克思主义理论学习与研究的热情高涨&#xff0c;也彰显了学院在人才培养、学科建设及学术研究等方面的不断进步与成就。 6…

Apache Seata应用侧启动过程剖析——注册中心与配置中心模块

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Apache Seata应用侧启动过程剖析——注册中心与配置中心模块 前言 在Seata的应用侧&#xf…

Docker逃逸CVE-2019-5736、procfs云安全漏洞复现,全文5k字,超详细解析!

Docker容器挂载procfs 逃逸 procfs是展示系统进程状态的虚拟文件系统&#xff0c;包含敏感信息。直接将其挂载到不受控的容器内&#xff0c;特别是容器默认拥有root权限且未启用用户隔离时&#xff0c;将极大地增加安全风险。因此&#xff0c;需谨慎处理&#xff0c;确保容器环…

最适合mysql5.6安装的linux版本-实战

文章目录 一, 适合安装mysql5.6的linu版本1. CentOS 72. Ubuntu 14.04 LTS (Trusty Tahr)3. Debian 8 (Jessie)4. Red Hat Enterprise Linux (RHEL) 7 二, 具体以Ubuntu 14.04 LTS (Trusty Tahr)为例安装虚拟机安装Ubuntu 14.04 LTS (Trusty Tahr) 自己弄安装ssh(便于远程访问,…

入职字节外包2个月后,我离职了...

有一种打工人的羡慕&#xff0c;叫做“大厂”。 真是年少不知大厂香&#xff0c;错把青春插稻秧。 但是&#xff0c;在深圳有一群比大厂员工更庞大的群体&#xff0c;他们顶着大厂的“名”&#xff0c;做着大厂的工作&#xff0c;还可以享受大厂的伙食&#xff0c;却没有大厂…

短视频商城系统源码揭秘:架构设计与实现

在短视频平台和电商平台蓬勃发展的背景下&#xff0c;短视频商城系统应运而生&#xff0c;融合了短视频内容和电商功能&#xff0c;给用户带来了全新的购物体验。本文将揭示短视频商城系统的源码架构设计与实现&#xff0c;帮助开发者了解该系统的内部工作原理及其关键技术。 …

C++11中新特性介绍-之(二)

11.自动类型推导 (1) auto类型自动推导 auto自动推导变量的类型 auto并不代表某个实际的类型&#xff0c;只是一个类型声明的占位符 auto并不是万能的在任意场景下都能推导&#xff0c;使用auto声明的变量必须进行初始化&#xff0c;以让编译器推导出它的实际类型&#xff0c;…

自然之美无需雕琢

《自然之美&#xff0c;无需雕琢 ”》在这个颜值至上的时代&#xff0c;但在温馨氛围中&#xff0c;单依纯以一种意想不到的方式&#xff0c;为我们诠释了自然之美的真谛。而医生的回答&#xff0c;如同一股清流耳目一新。“我说医生你看我这张脸&#xff0c;有没有哪里要动的。…

团队编程:提升代码质量与知识共享的利器

目录 前言1. 什么是团队编程&#xff1f;1.1 团队编程的起源1.2 团队编程的工作流程 2. 团队编程的优势2.1 提高代码质量2.2 促进知识共享2.3 增强团队协作2.4 提高开发效率 3. 团队编程的挑战3.1 开发成本较高3.2 需要良好的团队协作3.3 个人风格和习惯的差异3.4 长时间的集中…

2024亚太杯数学建模竞赛(B题)的全面解析

你是否在寻找数学建模比赛的突破点&#xff1f;数学建模进阶思路&#xff01; 作为经验丰富的数学建模团队&#xff0c;我们将为你带来2024亚太杯数学建模竞赛&#xff08;B题&#xff09;的全面解析。这个解决方案包不仅包括完整的代码实现&#xff0c;还有详尽的建模过程和解…

Docker:一、安装与卸载、配置阿里云加速器(Ubuntu)

目录 &#x1f341;安装docker&#x1f332;1、环境准备&#x1f332;2、安装docker Engine&#x1f9ca;1、卸载旧版、任何冲突的包&#x1f9ca;2、使用存储库安装&#x1f9ca;3、安装 Docker 包。&#x1f9ca;4、查询是否安装成功&#x1f9ca;5、运行hello-world镜像&…