【C语言课设】通讯录(静态、动态、文件)

文章详细介绍了如何使用C语言实现一个简单的通讯录系统,包括静态版和动态版。静态版使用固定大小的数组存储联系人信息,动态版则采用动态内存分配以适应任意数量的联系人,并实现了文件读写功能,使得数据持久化。此外,还涉及了结构体、菜单选择、排序等功能的实现。
摘要由CSDN通过智能技术生成

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前是C语言学习者
✈️专栏:课设
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注


前言

结构体知识回顾:点击跳转
这篇博客将带你用结构体相关知识来写一个简单的通讯录

一、通讯录的要求

  1. 包含人的相关信息:姓名、年龄、性别、地址和电话
  2. 假设通讯录要存放100个人(具体情况自己定)
  3. 功能:
  4. 增加联系人
  5. 删除指定联系人
  6. 查找指定联系人的信息
  7. 修改指定联系人的信息
  8. 显示所有人的信息
  9. 可以按照联系人的年龄或者按照名字排序(具体情况自己定)

二、创建文件

为了方便管理,我们可以创建多个文件来实现

  1. test.c - 测试通讯录 (源文件)
  2. contact.c - 通讯录的实现 (源文件)
  3. contact.h - 存放函数的声明 (头文件)
    在这里插入图片描述

三、静态版

3.1 创建菜单

按照我们的要求,其功能有6种,为了后期要选择相对应的功能,因此我们可以创建一个简易的菜单。

为了能使程序变得更简短而清晰,我们可以封装一个函数menu

【代码实现test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "contact.h"

void menu()                
{
	printf("**********************************\n");  //Add - 添加联系人								
	printf("****   1. Add     2. del      ****\n");  //del - 删除联系人
	printf("****   3. search  4. modify   ****\n");  //search - 查找指定联系人
	printf("****   5. show    6. sort     ****\n");  //modify - 修改指定联系人
	printf("****        0. exit           ****\n");  //show - 展示联系人
	printf("**********************************\n");  //sort - 对通讯录排序
													 //exit - 退出程序
}
int main()
{
	do
	{
		//创建菜单
		menu();

	} while ();
	return 0;
}

3.2 功能选择逻辑实现

创建好菜单后,还要能选择菜单上的功能

【代码实现test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include "contact.h"
void menu()                
{
	printf("**********************************\n");  //Add - 添加联系人
	printf("****   1. Add     2. del      ****\n");  //del - 删除联系人
	printf("****   3. search  4. modify   ****\n");  //search - 查找联系人
	printf("****   5. show    6. sort     ****\n");  //modify - 修改联系人
	printf("****        0. exit           ****\n");  //show - 展示联系人
	printf("**********************************\n");  //sort - 排序
													 //exit - 退出程序
}
int main()
{
	int input = 0;
	do
	{
		//创建菜单
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			
			break;
		case 2:
			
			break;
		case 3:
			
			break;
		case 4:
			
			break;
		case 5:
			
			break;
		case 6:
			
			break;
		case 0:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

这里的input可以输入任何值,输入1~6,用户则可以选择相对应的功能,但万一用户不小心输入其他值时,我们可以提醒“选择错误,重新选择”,这时switch语句就派上用场了。最后do while循环条件是input,大家想想,假如input为0,0为假,则结束循环,这不就和退出程序相互匹配上了。(功能的实现还未完善)

3.3 创建用户信息

根据要求,人的信息必须包含姓名、年龄、性别、地址和电话。因为在信息中,有charint不同的类型,因此我们可以创建一个结构体(概念:结构是一些值的集合,这些值可以称为成员变量结构的每个成员可以是不同类型的变量。)然后我们把类型的定义放到contact.h

【代码实现contact.h

//用户信息
typedef struct PeoInfo
{
	char name[20];//名字
	int age; //年龄
	char sex[5]//性别
	char addr[30] //地址
	char tele[12]; //电话号码
}PeoInfo;

上面的代码还能优化,使用#define

#define MAX 100     //通讯录人数的最大值
#define NAME_MAX 20 //人名字符串的最大值
#define SEX_MAX 5   //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值

//用户的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];    //名字
	int age;                //年龄
	char sex[SEX_MAX];	    //性别
	char addr[ADDR_MAX];	//地址
	char tele[TELE_MAX];    //电话
}PeoInfo;

优化的好处是:万一下次要改变数值的大小就方便多了

3.4 创建通讯录

定义完用户信息后,接下来就要存放100个人的信息,然后还要记录下当前已经存放的人的信息的个数。那该如何创建通讯录呢?一个是结构体类型、一个是int类型,所以我们再封装一个结构体。老样子,类型的声明和定义我们都放在contact.h

contact.h

#define MAX 100 //人的个数最大值
#define NAME_MAX 20//人名字符串的最大值
#define SEX_MAX 5  //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];    //名字
	int age;                //年龄
	char sex[SEX_MAX];	    //性别
	char addr[ADDR_MAX];	//地址
	char tele[TELE_MAX];    //电话
}PeoInfo;

//创建通讯录
typedef struct Contact
{
	PeoInfo data[MAX]; //存放人的信息
	int sz;  //表示当前通讯录有多少个人的信息
}Contact;

接下来就是创建通讯录以及它的初始化

【创建通讯录test.c

在这里插入图片描述

【初始化通讯录test.c

可以封装一个函数来帮助我们初始化(&结构体变量的效率更高)

在这里插入图片描述

初始化通讯录函数的声明放到contact.h

在这里插入图片描述

然后前期又创建一个contact.c文件,它的作用就是用来实现通讯录

在这里插入图片描述

2个可能有疑惑的问题

  1. 头文件:
    由于结构体类型的定义在contact.h中,而我们又想在另外一个文件中使用contact.h的内容,就要包含头文件,而又因为这个头文件不是C语言函数库提供的,所以要用双引号
    2.初始化通讯录
    创建完变量就初始化是一个非常好的编程习惯!但有很多人疑问为什么不这样初始化Contact con = {0},其实这样是可以的,但是这种方法太暴力了。为了提高逼格,我们可以封装一个函数来对其初始化。而函数的声明我们还是要放到contact.h

3.5 完善通讯录功能1 — 增加联系人

首先在test.c封装一个函数

test.c

在这里插入图片描述

对应的函数的声明,我们定义在contact.h

contact.h

在这里插入图片描述

Add函数的实现

contact.c

在这里插入图片描述

3.6 完善通讯录功能5 — 显示联系人的信息

显示联系人的信息无非就是打印嘛,那就非常简单了,只需要把通讯录内容遍历打印就行。

首先在test.c封装一个show函数

在这里插入图片描述

然后再对其进行函数的声明,声明就在contact.h中。

在这里插入图片描述

最后实现就在contact.c即可

在这里插入图片描述

接下来我们现在来调试,像代码量大的工程一定要学会调试,不然写到后面发现一堆bug,那就非常尴尬了。
因为我们上一步已经把添加联系人(Add函数)写好了,现在利用show来看看代码效果
在这里插入图片描述

3.7 完善通讯录功能2 — 删除指定联系人

首先现在test.c中封装del函数

在这里插入图片描述

然后在contact.h对其声明

在这里插入图片描述

最后,在contact.c里实现

void del(Contact* pc)
{
	char name[NAME_MAX];
	//记录要删除用户的位置,因为我们的删除方法是从后往前覆盖要删除的对象
	int position = 0; 
	//通讯录可能一位联系人都没有
	if (pc->sz == 0)
	{
		printf("您的通讯录没有联系人,无法删除\n");
		return;
	}
	//通讯录有人
	//1.找到要删除的人
	printf("请输入您要删除的对象:");
	scanf("%s", name);
	//2.查找通讯录是否有这个人(遍历)
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
		{
			position = i;
			break;
		}
	}
	//3.删除(将后面的人覆盖掉要删除的对象)
	for (int i = position; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

上面的代码其实还可以再优化一下,大家想:后面还要完善查找联系人信息和修改联系人信息的功能,它们有一个共同的步骤都是要先查找有没有对应的联系人,因此我们可以将查找这一步封装成一个函数。


int fine_by_name(Contact* pc,char name[])
{
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
		{
			return i;//返回下标
		}
	}
	return -1;
}
void del(Contact* pc)
{
	char name[NAME_MAX];
	if (pc->sz == 0)
	{
		printf("您的通讯录没有用户,无法删除\n");
		return;
	}
	//1.找到要删除的人
	printf("请输入您要删除的对象:");
	scanf("%s", name);
	
	//2.查找通讯录是否有这个人(查找函数)
	int res = fine_by_name(pc, name);
	if (res == -1)
	{
		printf("您的通讯录没有这个人\n");
		return;
	}
	
	//3.删除(将后面的人覆盖掉要删除的对象)
	for (int i = res; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

3.8 完善通讯录功能3 — 查找指定联系人

test.c封装一个search函数

在这里插入图片描述

然后在contact.h进行函数的声明

在这里插入图片描述

最后在contact.c实现

在这里插入图片描述

3.8 完善通讯录功能4 — 修改指定联系人的信息

首先在test.c封装一个modify函数

在这里插入图片描述

然后在contact.h进行声明

在这里插入图片描述

最后再contact.c中实现即可

void modify(Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要修改人的名字:");
	scanf("%s", name);
	//1.查找
	int res = fine_by_name(pc, name);
	if (-1 == res)
	{
		printf("您要修改的对象不存在\n");
		return;
	}
	//2.修改(重新输入)
	printf("请输入名字:");
	scanf("%s", pc->data[res].name);

	printf("请输入年龄:");
	scanf("%d", &(pc->data[res].age));

	printf("请输入性别:");
	scanf("%s", pc->data[res].sex);

	printf("请输入地址:");
	scanf("%s", pc->data[res].addr);

	printf("请输入电话号码:");
	scanf("%s", pc->data[res].tele);

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

3.10 完善通讯录功能6 — 排序

对于排序大家可以用很多种方式实现,我这里使用的是qsort函数,毕竟是我前几天刚刚学的知识。如果大家也想了解qsort,可以看看我往期的博客 -> 传送门

test.c部分

在这里插入图片描述

contact.h部分

在这里插入图片描述

contact.c部分

在这里插入图片描述

四、动态版

动态版解决了静态版最大的痛点——最大容量不好设置,动态版通讯录用到了动态内存管理的知识,遵循用多少、申请多少的原则,动态版通讯录能够有无限空间且不会造成浪费。

4.1 重新设计结构体类型

为了满足动态内存开辟的需求,只需将静态版通讯录中的结构体类型进行了重新设计,将原来的数组模式改为指针类型(动态开辟空间),同时需要新增容量这个成员,当下标等于容量时,进行扩容。

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define DEFAULT_SIZE 3
#define ADD_SIZE 2
#define MAX 100 //人的个数最大值
#define NAME_MAX 20//人名字符串的最大值
#define SEX_MAX 5  //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值


//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];     //名字
	int age;           //年龄
	char sex[SEX_MAX];	  //性别
	char addr[ADDR_MAX];	  //地址
	char tele[TELE_MAX];    //电话
}PeoInfo;

//创建通讯录
typedef struct Contact
{
	PeoInfo* data; //存放人的信息
	int sz;//表示当前通讯录有多少个人的信息
	int capacity; // 容量
}Contact;

4.2 枚举常量

其实枚举常量对动态开辟没什么关系,它的存在可以使case语句更清晰,不用依赖于数字,而是见名知意,是一种提高程序可读性的好方法。默认从0开始往后枚举。

enum Option
{
	EXIT, // 0
	ADD, // 1
	DEL, // 2
	SEARCH, // 3
	MODIFY, // 4
	SHOW, // 5 
	SORT // 6
};

int main()
{
	int input = 0;
	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);
	do
	{
		//创建菜单
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			Add(&con);
			break;
		case DEL:
			del(&con);
			break;
		case SEARCH:
			search(&con);
			break;
		case MODIFY:
			modify(&con);
			break;
		case SHOW:
			show(&con);
			break;
		case SORT:
			sort(&con);
			break;
		case EXIT:
			FREE(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

4.3 通讯录的初始化

动态版通讯录在初始化时,需要先申请默认大小的空间,容量也要设为默认值

#define DEFAULT_SIZE 3

void InitContact(Contact* pc)
{
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SIZE, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact :: calloc");
		return;
	}
	pc->data = ptr;
	ptr = NULL;
	pc->capacity = DEFAULT_SIZE;
}

4.4 增加Add

每增加一位联系人都需要考虑扩容的情况

#define ADD_SIZE 2
// 封装一个函数来检查容量
void checkCapacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->sz + ADDR_MAX) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("Contact :: realloc");
			return;
		}
		pc->data = ptr;
		ptr = NULL;
		pc->capacity += ADDR_MAX;
	}
}

void Add(Contact* pc)
{
	// 检查容量
	checkCapacity(pc);
	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);//name是数组名

	printf("请输入年龄:");
	scanf("%d", &(pc->data[pc->sz].age));

	printf("请输入性别:");
	scanf("%s", pc->data[pc->sz].sex);//sex是数组名

	printf("请输入地址:");
	scanf("%s", pc->data[pc->sz].addr);//addr是数组名

	printf("请输入电话号码:");
	scanf("%s", pc->data[pc->sz].tele);//tele是数组名

	pc->sz++; //增加一个人对于的sz也要增加
}

4.5 释放动态开辟的空间

需要注意的是动态开辟的空间,在通讯录结束时要归还给操作系统。

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

五、文件版

文件版是在动态版的基础上进行了改进,即使退出了程序,联系人还是存在,这会用到文件操作相关知识:点击跳转

5.1 文件加载

程序之前结束可能已经添加了联系人,因此文件加载函数可以放在初始化函数中,当然文件加载前还需要判断当前通讯录容量是否足够,因此需要对扩容函数进行声明确保其能在初始化函数中使用。

// 文件加载函数
void Load(Contact* pc)
{
	assert(pc);
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	Peoinfo tmp = { 0 };
	int i = 0;
	while(fread(&tmp, sizeof(Peoinfo), 1, pf))
	{
		// 增容
		if (pc->size == pc->capacity)
		{
			Peoinfo* p = (Peoinfo*)realloc(pc->data, (pc->capacity + ADD_SIZE) * sizeof(Peoinfo));
			if (p == NULL)
			{
				perror("realloc");
				return;
			}
			pc->data = p;
			pc->capacity += ADD_SIZE;
		}
		pc->data[i] = tmp;
		pc->size++;
		i++;
	}
	fclose(pf);
	pf = NULL;
}

// 初始化通讯录
void ContactInit(Contact* pc)
{
	/*assert(pc);
	pc->size = 0;
	memset(pc->data, 0, sizeof(pc->data));*/

	// 动态版
	assert(pc);
	pc->size = 0;
	Peoinfo* p = (Peoinfo*)calloc(DEFAULT_SIZE, sizeof(Peoinfo));
	if (p == NULL)
	{
		perror("calloc");
		return;
	}
	pc->data = p;
	pc->capacity = DEFAULT_SIZE;

	// 加载通讯录
	Load(pc);
}

注意:文件加载遵循文件打开三步走,即打开文件、使用文件、关闭文件。在打开文件后,要对文件指针进行空指针判断。加载文件时,会读取文件中的标头信息,在循环读取通讯录数据,这里采用了格式化读取,每读取成功一个数据,下标+1。

5.2 保存联系人

保存联系人就是文件写入操作,将当前程序中结构体的数据写入到文件中,正式写入数据前需要先写入标头信息,通过循环将通讯录中的数据全部写入文件中。

// 保存文件
void Save(Contact* pc)
{
	assert(pc);
	FILE* pf = fopen("Contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	else
	{
		// 写 
		for (int i = 0; i < pc->size; i++)
		{
			fwrite(pc->data + i, sizeof(Peoinfo), 1, pf);
		}
		fclose(pf);
		pf = NULL;
	}
}

注意:文件写入的操作指令是·"w" ,指令给错后将无法写入数据。格式化写入同格式化读取一样,格式一定要匹配上。记得关闭文件,并把文件指针置空。

六、源码部分

6.1 静态版

【test.c】

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include "contact.h"
void menu()                
{
	printf("**********************************\n");  //Add - 添加联系人
	printf("****   1. Add     2. del      ****\n");  //del - 删除联系人
	printf("****   3. search  4. modify   ****\n");  //search - 查找联系人
	printf("****   5. show    6. sort     ****\n");  //modify - 修改联系人
	printf("****        0. exit           ****\n");  //show - 展示联系人
	printf("**********************************\n");  //sort - 排序
													 //exit - 退出程序
}
int main()
{
	int input = 0;
	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);//结构体传参
	do
	{
		//创建菜单
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Add(&con);
			break;
		case 2:
			del(&con);
			break;
		case 3:
			search(&con);
			break;
		case 4:
			modify(&con);
			break;
		case 5:
			show(&con);
			break;
		case 6:
			sort(&con);
			break;
		case 0:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

【contact.h】

#define _CRT_SECURE_NO_WARNINGS 1
#define MAX 100 //人的个数最大值
#define NAME_MAX 20//人名字符串的最大值
#define SEX_MAX 5  //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值


//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];     //名字
	int age;           //年龄
	char sex[SEX_MAX];	  //性别
	char addr[ADDR_MAX];	  //地址
	char tele[TELE_MAX];    //电话
}PeoInfo;

//创建通讯录
typedef struct Contact
{
	
	PeoInfo data[MAX]; //存放人的信息
	int sz;//表示当前通讯录有多少个人的信息
}Contact;

//初始化通讯录函数
void InitContact(Contact* pc);

//增加联系人
void Add(Contact* pc);
//显示联系人
void show(Contact* pc);
//删除联系人
void del(Contact* pc);
//查找联系人
void search(Contact* pc);
//修改联系人
void modify(Contact* pc);
//排序
void sort(Contact* pc);

【contact.c】

#define _CRT_SECURE_NO_WARNINGS 1
#include <string.h>

#include <stdlib.h>
#include "contact.h"

//通讯录的初始化
void InitContact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data)); //初始化为0
}


void Add(Contact* pc)
{
	if (pc->sz == MAX) //MAX在头文件定义了,表示通讯录最大人数
	{
		printf("添加失败,通讯录空间已满\n");
		return;
	}
	//否则就能增加一个人的信息

	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);//name是数组名

	printf("请输入年龄:");
	scanf("%d", &(pc->data[pc->sz].age));

	printf("请输入性别:");
	scanf("%s", pc->data[pc->sz].sex);//sex是数组名

	printf("请输入地址:");
	scanf("%s", pc->data[pc->sz].addr);//addr是数组名

	printf("请输入电话号码:");
	scanf("%s", pc->data[pc->sz].tele);//tele是数组名

	pc->sz++; //增加一个人对于的sz也要增加
}

void show(Contact* pc)
{
	printf("%s\t%s\t%s\t%s\t%s\n", "名字", "年龄", "性别", "地址", "电话");
	for (int i = 0; i < pc->sz; i++)
	{
		printf("%s\t%d\t%s\t%s\t%s\n", pc->data[i].name,
									   pc->data[i].age,
									   pc->data[i].sex,
									   pc->data[i].addr,
									   pc->data[i].tele);
	}
}


int fine_by_name(Contact* pc,char name[])
{
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
		{
			return i;
		}
	}
	return -1;
}
void del(Contact* pc)
{
	char name[NAME_MAX];
	if (pc->sz == 0)
	{
		printf("您的通讯录没有用户,无法删除\n");
		return;
	}
	//通讯录有人删除
	//1.找到要删除的人
	printf("请输入您要删除的对象:");
	scanf("%s", name);
	
	//2.查找通讯录是否有这个人
	//查找函数
	int res = fine_by_name(pc, name);
	if (res == -1)
	{
		printf("您的通讯录没有这个人\n");
		return;
	}
	
	//3.删除(将后面的人覆盖掉要删除的对象)
	for (int i = res; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}


void search(Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要查找人的名字:");
	scanf("%s", name);

	//利用上一步的查找函数
	int res = fine_by_name(pc, name);
	if (-1 == res)
	{
		printf("您查找的该用户不存在\n");
		return;
	}
	//找到了就打印
	printf("%s\t%s\t%s\t%s\t%s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%s\t%d\t%s\t%s\t%s\n", pc->data[res].name,
		pc->data[res].age,
		pc->data[res].sex,
		pc->data[res].addr,
		pc->data[res].tele);
}

void modify(Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要修改人的名字:");
	scanf("%s", name);
	//1.查找
	int res = fine_by_name(pc, name);
	if (-1 == res)
	{
		printf("您要修改的对象不存在\n");
		return;
	}
	//2.找到(重新输入)
	printf("请输入名字:");
	scanf("%s", pc->data[res].name);

	printf("请输入年龄:");
	scanf("%d", &(pc->data[res].age));

	printf("请输入性别:");
	scanf("%s", pc->data[res].sex);

	printf("请输入地址:");
	scanf("%s", pc->data[res].addr);

	printf("请输入电话号码:");
	scanf("%s", pc->data[res].tele);

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

int cmp_name(void* p1, void* p2)
{
	return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}

int cmp_age(void* p1, void* p2)
{
	return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}
void sort(Contact* pc)
{
	int input = 0;
	printf("请选择排序方式(默认从小到大):1(年龄)/2(姓名):");
	scanf("%d", &input);
	if (input == 2)
		qsort(pc, pc->sz, sizeof(PeoInfo), cmp_name);
	else
		qsort(pc, pc->sz, sizeof(PeoInfo), cmp_age);
	printf("恭喜你排序成功,请在选择show中观察效果\n");
}

6.2 动态版

【test.c】

#define _CRT_SECURE_NO_WARNINGS 1


#include "contact.h"
void menu()
{
	printf("**********************************\n");  //Add - 添加联系人
	printf("****   1. Add     2. del      ****\n");  //del - 删除联系人
	printf("****   3. search  4. modify   ****\n");  //search - 查找联系人
	printf("****   5. show    6. sort     ****\n");  //modify - 修改联系人
	printf("****        0. exit           ****\n");  //show - 展示联系人
	printf("**********************************\n");  //sort - 排序
													 //exit - 退出程序
}

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
int main()
{
	int input = 0;
	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);
	do
	{
		//创建菜单
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			Add(&con);
			break;
		case DEL:
			del(&con);
			break;
		case SEARCH:
			search(&con);
			break;
		case MODIFY:
			modify(&con);
			break;
		case SHOW:
			show(&con);
			break;
		case SORT:
			sort(&con);
			break;
		case EXIT:
			FREE(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

【const.h】

#pramga once
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define DEFAULT_SIZE 3
#define ADD_SIZE 2
#define MAX 100 //人的个数最大值
#define NAME_MAX 20//人名字符串的最大值
#define SEX_MAX 5  //性别字符的最大值
#define ADDR_MAX 30 //地址字符的最大值
#define TELE_MAX 12 //电话字符的最大值


//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];     //名字
	int age;           //年龄
	char sex[SEX_MAX];	  //性别
	char addr[ADDR_MAX];	  //地址
	char tele[TELE_MAX];    //电话
}PeoInfo;

//创建通讯录
typedef struct Contact
{
	PeoInfo* data; //存放人的信息
	int sz;//表示当前通讯录有多少个人的信息
	int capacity;
}Contact;

//初始化通讯录函数
void InitContact(Contact* pc);

//增加联系人
void Add(Contact* pc);
//显示联系人
void show(Contact* pc);
//删除联系人
void del(Contact* pc);
//查找联系人
void search(Contact* pc);
//修改联系人
void modify(Contact* pc);
//排序
void sort(Contact* pc);
//释放空间
void FREE(Contact* pc);

【contact.c】

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

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

}
//通讯录的初始化
void InitContact(Contact* pc)
{
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SIZE, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact :: calloc");
		return;
	}
	pc->data = ptr;
	ptr = NULL;
	pc->capacity = DEFAULT_SIZE;
}


void Add(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->sz + ADDR_MAX) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("Contact :: realloc");
			return;
		}
		pc->data = ptr;
		ptr = NULL;
		pc->capacity += ADDR_MAX;
	}

	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);//name是数组名

	printf("请输入年龄:");
	scanf("%d", &(pc->data[pc->sz].age));

	printf("请输入性别:");
	scanf("%s", pc->data[pc->sz].sex);//sex是数组名

	printf("请输入地址:");
	scanf("%s", pc->data[pc->sz].addr);//addr是数组名

	printf("请输入电话号码:");
	scanf("%s", pc->data[pc->sz].tele);//tele是数组名

	pc->sz++; //增加一个人对于的sz也要增加
}

void show(Contact* pc)
{
	printf("%s\t%s\t%s\t%s\t%s\n", "名字", "年龄", "性别", "地址", "电话");
	for (int i = 0; i < pc->sz; i++)
	{
		printf("%s\t%d\t%s\t%s\t%s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].addr,
			pc->data[i].tele);
	}
}


int fine_by_name(Contact* pc, char name[])
{
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0) //=0说明找到了
		{
			return i;
		}
	}
	return -1;
}
void del(Contact* pc)
{
	char name[NAME_MAX];
	if (pc->sz == 0)
	{
		printf("您的通讯录没有用户,无法删除\n");
		return;
	}
	//通讯录有人删除
	//1.找到要删除的人
	printf("请输入您要删除的对象:");
	scanf("%s", name);

	//2.查找通讯录是否有这个人
	//查找函数
	int res = fine_by_name(pc, name);
	if (res == -1)
	{
		printf("您的通讯录没有这个人\n");
		return;
	}

	//3.删除(将后面的人覆盖掉要删除的对象)
	for (int i = res; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}


void search(Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要查找人的名字:");
	scanf("%s", name);

	//利用上一步的查找函数
	int res = fine_by_name(pc, name);
	if (-1 == res)
	{
		printf("您查找的该用户不存在\n");
		return;
	}
	//找到了就打印
	printf("%s\t%s\t%s\t%s\t%s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%s\t%d\t%s\t%s\t%s\n", pc->data[res].name,
		pc->data[res].age,
		pc->data[res].sex,
		pc->data[res].addr,
		pc->data[res].tele);
}

void modify(Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要修改人的名字:");
	scanf("%s", name);
	//1.查找
	int res = fine_by_name(pc, name);
	if (-1 == res)
	{
		printf("您要修改的对象不存在\n");
		return;
	}
	//2.找到(重新输入)
	printf("请输入名字:");
	scanf("%s", pc->data[res].name);

	printf("请输入年龄:");
	scanf("%d", &(pc->data[res].age));

	printf("请输入性别:");
	scanf("%s", pc->data[res].sex);

	printf("请输入地址:");
	scanf("%s", pc->data[res].addr);

	printf("请输入电话号码:");
	scanf("%s", pc->data[res].tele);

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

int cmp_name(void* p1, void* p2)
{
	return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}

int cmp_age(void* p1, void* p2)
{
	return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}

void sort(Contact* pc)
{
	int input = 0;
	printf("请选择排序方式(默认从小到大):1(年龄)/2(姓名):");
	scanf("%d", &input);
	if (input == 2)
		qsort(pc, pc->sz, sizeof(PeoInfo), cmp_name);
	else
		qsort(pc, pc->sz, sizeof(PeoInfo), cmp_age);
	printf("恭喜你排序成功,请在选择show中观察效果\n");
}

6.3 静态版

【contact.h】

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

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 20
#define ADDRESS_MAX 20
#define Peo_MAX 1000

#define DEFAULT_SIZE 3 // 通讯录的默认大小
#define ADD_SIZE 2 // 每次扩容的大小

typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tele[TELE_MAX];
	char address[ADDRESS_MAX];
}Peoinfo;

typedef struct Contact
{
	//Peoinfo data[Peo_MAX];
	//int size;
	 
	// 动态版
	Peoinfo* data;
	int size;
	int capacity;
}Contact;

// 初始化通讯录
void ContactInit(Contact* pc);

// 添加
void add(Contact* pc);


// 显示联系人
void show(Contact* pc);

// 删除联系人
void del(Contact* pc);

// 查询联系人
void search(Contact* pc);

// 修改联系人
void modify(Contact* pc);

// 清空联系人
void clear(Contact* pc);

// 按名字排序
void Sort(Contact* pc);

// 销毁
void Free(Contact* pc);

// 保存文件
void Save(Contact* pc);

【contact.c】

#include "Conntact.h"

void Load(Contact* pc)
{
	assert(pc);
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	Peoinfo tmp = { 0 };
	int i = 0;
	while(fread(&tmp, sizeof(Peoinfo), 1, pf))
	{
		// 增容
		if (pc->size == pc->capacity)
		{
			Peoinfo* p = (Peoinfo*)realloc(pc->data, (pc->capacity + ADD_SIZE) * sizeof(Peoinfo));
			if (p == NULL)
			{
				perror("realloc");
				return;
			}
			pc->data = p;
			pc->capacity += ADD_SIZE;
		}
		pc->data[i] = tmp;
		pc->size++;
		i++;
	}
	fclose(pf);
	pf = NULL;
}


// 初始化通讯录
void ContactInit(Contact* pc)
{
	/*assert(pc);
	pc->size = 0;
	memset(pc->data, 0, sizeof(pc->data));*/

	// 动态版
	assert(pc);
	pc->size = 0;
	Peoinfo* p = (Peoinfo*)calloc(DEFAULT_SIZE, sizeof(Peoinfo));
	if (p == NULL)
	{
		perror("calloc");
		return;
	}
	pc->data = p;
	pc->capacity = DEFAULT_SIZE;

	// 加载通讯录
	Load(pc);
}

// 添加
void add(Contact* pc)
{
	/*assert(pc);
	
	if (pc->size == Peo_MAX)
	{
		printf("通讯录已满,添加失败");
		return;
	}

	printf("联系人姓名:");
	scanf("%s", pc->data[pc->size].name);

	printf("联系人性别:");
	scanf("%s", pc->data[pc->size].sex);

	printf("联系人年龄:");
	scanf("%d", &(pc->data[pc->size].age));

	printf("联系人电话:");
	scanf("%s", pc->data[pc->size].tele);

	printf("联系人地址:");
	scanf("%s", pc->data[pc->size].address);

	pc->size++;

	printf("添加成功\n");*/

	// 动态版
	assert(pc);

	if (pc->size == pc->capacity)
	{
		// 增容
		Peoinfo* p = (Peoinfo*)realloc(pc->data, (pc->capacity + ADD_SIZE) * sizeof(Peoinfo));
		if (p == NULL)
		{
			perror("realloc");
			return;
		}
		pc->data = p;
		pc->capacity += ADD_SIZE;
	}

	printf("联系人姓名:");
	scanf("%s", pc->data[pc->size].name);

	printf("联系人性别:");
	scanf("%s", pc->data[pc->size].sex);

	printf("联系人年龄:");
	scanf("%d", &(pc->data[pc->size].age));

	printf("联系人电话:");
	scanf("%s", pc->data[pc->size].tele);

	printf("联系人地址:");
	scanf("%s", pc->data[pc->size].address);

	pc->size++;

	printf("添加成功\n");
}

// 显示联系人
void show(Contact* pc)
{
	assert(pc);

	printf("姓名\t性别\t年龄\t电话\t地址\n");

	for (int i = 0; i < pc->size; i++)
	{
		printf("%s\t%s\t%d\t%s\t%s\n", pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].address);
	}
}


static int find_by_name(Contact* pc, char name[])
{
	assert(pc);
	for (int i = 0; i < pc->size; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

// 删除联系人
void del(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };

	// 当前通讯录可能一个联系人都没有
	if (pc->size == 0)
	{
		printf("没有联系人,无法删除\n");
		return;
	}

	printf("请输入删除人的姓名:>");
	scanf("%s", name);

	// 该联系人可能不存在(查找联系人)
	// 找不到返回-1。注意不能返回0,因为返回的下标可能为0
	// 找到返回0
	int res = find_by_name(pc, name);
	if (res == -1)
	{
		printf("该联系人不存在,无法删除\n");
		return;
	}
	else
	{
		// 删除
		for (int i = res; i < pc->size - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		pc->size--;
		printf("删除成功\n");
	}
}

// 查询联系人
void search(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = "0";
	printf("请输入姓名:>");
	scanf("%s", name);
	int res = find_by_name(pc, name);
	if (res == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	printf("姓名\t性别\t年龄\t电话\t地址\n");

	printf("%s\t%s\t%d\t%s\t%s\n", pc->data[res].name,
		pc->data[res].sex,
		pc->data[res].age,
		pc->data[res].tele,
		pc->data[res].address);
}



// 修改联系人
void modify(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("该通讯录给空\n");
		return;
	}
	char name[NAME_MAX] = { 0 };
	printf("请输入修改者的姓名:>");
	scanf("%s", name);
	int res = find_by_name(pc, name);
	if (res == -1)
	{
		printf("该联系人不存在\n");
		return;
	}
	
	printf("联系人姓名:");
	scanf("%s", pc->data[res].name);

	printf("联系人性别:");
	scanf("%s", pc->data[res].sex);

	printf("联系人年龄:");
	scanf("%d", &(pc->data[res].age));

	printf("联系人电话:");
	scanf("%s", pc->data[res].tele);

	printf("联系人地址:");
	scanf("%s", pc->data[res].address);

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

// 清空联系人
void clear(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("通讯录没有联系人,快去添加吧 ~\n");
		return;
	}
	memset(pc->data, 0, sizeof(pc->data));
	pc->size = 0;

	printf("清空成功\n");
}

int cmp(const void* p1, const void* p2)
{
	assert(p1 && p2);
	return strcmp((*(Peoinfo*)p1).name, (*(Peoinfo*)p2).name);
}

// 按名字排序
void Sort(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("通讯录没有联系人,快去添加吧 ~\n");
		return;
	}
	qsort(pc->data, pc->size, sizeof(Peoinfo), cmp);
	printf("排序成功\n");
}


// 销毁
void Free(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->size = 0;
	pc = NULL;
}


// 保存文件
void Save(Contact* pc)
{
	assert(pc);
	FILE* pf = fopen("Contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	else
	{
		// 写 
		for (int i = 0; i < pc->size; i++)
		{
			fwrite(pc->data + i, sizeof(Peoinfo), 1, pf);
		}
		fclose(pf);
		pf = NULL;
	}
}

【test.c】

#include "Conntact.h"
void menu()
{
	printf("*******************************\n");
	printf("****  1. 添加     2. 删除   ****\n");
	printf("****  3. 查找     4. 修改   ****\n");
	printf("****  5. 显示     6. 清空   ****\n");
	printf("****  7. 排序     0. 退出   ****\n");
	printf("*******************************\n");
}

enum Option
{
	EXIT,
	ADD,
	DELETE,
	SEARCH,
	MODIFY,
	SHOW,
	CLEAR,
	SORT
};

int main()
{
	int n = 0;
	// 创建通讯录
	Contact con;
	// 初始化通讯录
	ContactInit(&con);
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &n);
		switch (n)
		{
		case EXIT:
			//保存文件信息到文件
			Save(&con);
			// 销毁开辟的空间
			Free(&con);
			printf("保存数据成功,已退出通讯录\n");
			break;
		case ADD:
			add(&con);
			break;
		case DELETE:
			del(&con);
			break;
		case SEARCH:
			search(&con);
			break;
		case MODIFY:
			modify(&con);
			break;
		case SHOW:
			show(&con);
			break;
		case CLEAR:
			clear(&con);
			break;
		case SORT:
			Sort(&con);
			break;
		}
	} while (n);

	return 0;
}
  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值