二十二、通讯录实现

1 基本功能

  1. 能够保存100个人的以下信息:姓名、年龄、性别、电话、地址。
  2. 增加联系人。
  3. 删除联系人。
  4. 查找指定联系人。
  5. 修改联系人信息。
  6. 显示所有联系人信息。

2 结构设计

contact.h:函数和类型的声明
contacy.c:函数的实现
test.c:测试通讯录

3 具体功能及代码实现

3.1 通讯录的结构

要实现通讯录,首先我们就要设计一下通讯录的结构,通讯录中有不同的联系人,而不同的联系人又有不同类型的信息,所以我们可以将联系人的信息统一放在一个结构体中。

由于这个类型我们在contacy.ctest.c两个文件中都会用到,所以我们可以直接在头文件contact.h中定义联系人类型,后续要使用这个类型时直接包含这个头文件即可。同理,其他库函数的头文件我们也可以直接放在contact.h中。

//定义一些常量,方便后期修改
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TALE 20
#define MAX_ADDR 30
//联系人类型的声明
typedef struct PeoInfo
{
	char name[MAX_NAME];
	size_t age;
	char sex[MAX_SEX];
	char tele[MAX_TALE];
	char addr[MAX_ADDR];
}PeoInfo;//将结构体类型struct PeoInfo重定义为PeoInfo

如果我们直接以联系人的类型定义一个数组作为通讯录来存储联系人的信息,那么由于数组的大小是提前确定的,如果我们想确定当前联系人的个数就比较麻烦,因此我们最好再创建一个通讯录类型来存储联系人的信息和当前联系人的个数。

//通讯录类型的声明
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz;//用来记录通讯录中的联系人个数
}Contact;//将结构体类型struct Contact重定义为Contact

设计好通讯录的结构,接下来我们就可以实现通讯录的具体功能了。

3.2 菜单

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

首先打印通讯录菜单供用户选择,输入0~5范围内的数程序就会根据对应的数去执行相应的函数,所以在主函数中应该还存在一个分支结构,这个结构负责对输入的结果进行判断,如果是1~5则运行通讯录相关的函数,是0就退出通讯录。

int main()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)//对用户输入的结果进行判断
		{
		case 1://结果为1,增加联系人
			AddContact(&con);
			break;
		case 2://结果为2,删除联系人
			DelContact(&con);
			break;
		case 3://结果为3,查找指定联系人
			SearchContact(&con);
			break;
		case 4://结果为4,修改联系人信息
			ModifyContact(&con);
			break;
		case 5://结果为5,显示所有联系人信息
			ShowContact(&con);
			break;
		case 0://结果为0,退出通讯录
			printf("退出通讯录\n");
			break;
		default://所有结果都不是,重新输入
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

实际上,在这里我们可以把我们刚学过的枚举类型利用起来。由于枚举常量的取值默认从0开始依次递增1,那么我们直接定义一个枚举类型,里面的常量依次对应0~5即可。

在这里我们将其定义在头文件中contact.h中:

//contact.h
enum OPTION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
};

定义好后,对原来的switch语句进行修改:

int main()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)//对用户输入的结果进行判断
		{
		case ADD://结果为1,增加联系人
			AddContact(&con);
			break;
		case DEL://结果为2,删除联系人
			DelContact(&con);
			break;
		case SEARCH://结果为3,查找指定联系人
			SearchContact(&con);
			break;
		case MODIFY://结果为4,修改联系人信息
			ModifyContact(&con);
			break;
		case SHOW://结果为5,显示所有联系人信息
			ShowContact(&con);
			break;
		case EXIT://结果为0,退出通讯录
			printf("退出通讯录\n");
			break;
		default://所有结果都不是,重新输入
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
}

这样一来,代码的可读性是不是就提高了。

3.3 初始化通讯录函数

当我们的代码运行起来时,首先要做的应该是将通讯录初始化。

在这里,我们可以利用memset函数能将ptr指向的内存块的前num个字节设置为value的特性将通讯录里面的所有值都初始化为0。

//memset函数的声明
void* memset(void* ptr, int value, size_t num);
//初始化通讯录函数
void InitContact(Contact* pc)
{
	assert(pc);
	memset(pc, 0, sizeof(pc->data));
	pc->sz = 0;//联系人人数初始化为0
}

3.4 增加联系人函数

将通讯录初始化后,我们就可以开始给通讯录里添加联系人了。

要实现这个功能,首先我们应该判断通讯录里的人数有没有满,如果满了就无法添加,如果没满,我们就可以依次输入联系人的相关信息,录入成功后,再将联系人个数加1即可。

//增加联系人函数
void AddContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 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);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;//录入成功后,联系人个数+1
	printf("成功添加联系人\n");
}

3.5 显示所有联系人信息函数

如果想看一看当前通讯录存了哪些联系人,我们就可以写一个函数通过遍历通讯录来实现,这一部分较为简单就不再过多赘述。

//显示所有联系人信息函数
void ShowContact(const Contact* pc)//由于打印通讯录不需要对其进行修改,因为我们可以加个const进行修饰
{
	assert(pc);
	int i = 0;
	//打印列标题
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	//打印数据
	for (i = 0; i < pc->sz; i++)
	{
		printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

3.6 查找指定联系人函数

当我们的通讯录里已经存储了一些联系人的信息后,如果想对指定联系人进行查找,就需要对通讯录里面的联系人进行遍历,如果当前位置的联系人的信息符合我们查找的条件,我们就标记这个联系人在通讯录中的位置(pos),而后打印出这个联系人的信息。在这里,我们以联系人的姓名作为我们查找的条件。

//查找指定联系人函数
void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	int pos = 0;
    int i = 0;
    int flag = 0;
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
    for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)//不能用pc->data[i].name==name来比较
		{
			pos = i;
			flag = 1;//表示找到了
			break;
		}
	}
	if (flag == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);
}	

在这里需要强调的是,查找联系人不能用pc->data[i].name==name作为判断条件,因为这里的name实际上表示的是数组的地址,如果用pc->data[i].name==name作为判断条件,由于两个数组的地址显然不同,那么这个等式也恒不成立,进而达不到想要的查找效果。

除此之外,这样写虽然能够实现查找的功能,但是有没有什么问题呢?

实际上,我们可以将查找功能的核心部分封装成一个函数,因为我们后续要实现删除联系人、修改联系人信息时,也需要先进行查找再进行相应的操作,如果每个函数都还要单独写一段查找的代码,显然就有些麻烦了,所以我们可以这样改。

//按姓名查找联系人函数
int FindByName(const Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;//如果找到就返回当前联系人的下标
		}
	}
	return -1;	//如果没找到就返回-1
}

这样一来,当我们其他函数也要需要查找时,直接调用FindByName函数即可。

有了这样一个FindByName函数后,我们就可以这么修改我们的SearchContact函数:

//查找指定联系人函数
void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	int pos = 0;
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);
}

3.7 删除联系人函数

要删除通讯录里面的联系人,首先我们应该判断通讯录是否为空,如果不为空我们才进行删除操作,先使用FindByName找到待删除联系人的位置,而后从这个位置(定义为pos)开始,将后面整体往前移一位将当前pos中存放的联系人信息覆盖掉,就实现了删除的效果。

void DelContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME];
	assert(pc);
	//删除
	printf("请输出待删除联系人的姓名:>");
	scanf("%s", name);
	//找到要删除的人
	int i = 0;
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (i = pos; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];//将pos后面的位置整体往前移一位
	}
	pc->sz--;//联系人数量减一
	printf("成功删除联系人\n");
}

3.8 修改指定联系人函数

和前面类似,要修改指定联系人的信息,首先我们应该判断通讯录是否为空,如果不为空我们才进行修改操作,先使用FindByName找到待修改联系人的位置,而后将这个位置里面的信息重新录入一遍即可。

void ModifyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	int pos = 0;
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的人不存在\n");
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);

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

4 通讯录完整代码

4.1 contact.h

#pragma once
#include 
#include 
#include 
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TALE 20
#define MAX_ADDR 30
enum OPTION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW
};
//联系人类型的声明
typedef struct PeoInfo
{
	char name[MAX_NAME];
	size_t age;
	char sex[MAX_SEX];
	char tele[MAX_TALE];
	char addr[MAX_ADDR];
}PeoInfo;//将结构体类型struct PeoInfo重定义为PeoInfo

//通讯录类型的声明
typedef struct Contact
{
	PeoInfo data[MAX];
	int sz;//用来记录通讯录中的联系人个数
}Contact;//将结构体类型struct Contact重定义为Contact

//函数的声明
void InitContact(Contact* pc);//初始化通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(const Contact* pc);//显示所有联系人信息
void DelContact(Contact* pc);//删除联系人
void SearchContact(const Contact* pc);//查找指定联系人
void ModifyContact(Contact* pc);//修改指定联系人

4.2 contact.c

#include "contact.h"
void InitContact(Contact* pc)
{
	assert(pc);
	memset(pc, 0, sizeof(pc->data));
	pc->sz = 0;
}

	void AddContact(Contact* pc)
	{
		assert(pc);
		if (pc->sz == 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);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);

		pc->sz++;//录入成功后,联系人个数+1
		printf("成功添加联系人\n");
	}

void ShowContact(const Contact* pc)//由于打印通讯录不需要对其进行修改,因为我们可以加个const进行修饰
{
	assert(pc);
	int i = 0;
	//打印列标题
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	//打印数据
	for (i = 0; i < pc->sz; i++)
	{
		printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

int FindByName(const Contact* pc, char name[])
{
	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)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME];
	assert(pc);
	//删除
	printf("请输出待删除联系人的姓名:>");
	scanf("%s", name);
	//找到要删除的人
	int i = 0;
	//删除位置在pos的联系人
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (i = pos; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];//将pos后面的位置整体往前移一位
	}
	pc->sz--;
	printf("成功删除联系人\n");
}

void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	int pos = 0;
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
			pc->data[pos].name,
			pc->data[pos].age,
			pc->data[pos].sex,
			pc->data[pos].tele,
			pc->data[pos].addr);
}

void ModifyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	int pos = 0;
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的人不存在\n");
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);

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

4.3 test.c

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

int main()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)//对用户输入的结果进行判断
		{
		case ADD://结果为1,增加联系人
			AddContact(&con);
			break;
		case DEL://结果为2,删除联系人
			DelContact(&con);
			break;
		case SEARCH://结果为3,查找指定联系人
			SearchContact(&con);
			break;
		case MODIFY://结果为4,修改联系人信息
			ModifyContact(&con);
			break;
		case SHOW://结果为5,显示所有联系人信息
			ShowContact(&con);
			break;
		case EXIT://结果为0,退出通讯录
			printf("退出通讯录\n");
			break;
		default://所有结果都不是,重新输入
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

5 后续优化

5.1 通讯录的结构优化

刚才我们设计的通讯录虽然已经具备了基本的功能,但是其实还有可优化的空间。比较明显的一点,就是我们刚才设计的通讯录的空间是固定的,要想让通讯录实现扩容的功能,我们可以考虑用上节刚学的动态内存函数来实现。

每次通讯录程序启动时,我们可以用malloc函数默认先开辟能存放3个人信息的空间,如果不够,再每次增加2个。

如果按照这样的想法,那么我们之前设计的通讯录类型就需要进行修改,data不应该是个数组而应该是个指向存放空间的指针。

//通讯录类型优化
typedef struct Contact
{
	PeoInfo* data;//指向存放数据的空间
	int sz;
	int capacity;//记录当前通讯录的最大容量
}Contact;

5.2 初始化通讯录函数的优化

通讯录的结构被调整后,那我们初始化通讯录的方式也需要进行改造。

之前的初始化的形式,是基于pc->data的类型为数组的情况通过循环的形式实现的,pc->data的类型调整为PeoInfo*类型之后,我们就可以用malloc来对其指向的空间进行开辟,如果开辟成功就尽心后续操作,否则报错。

//初始化通讯录函数优化
#define DEFAULT_SZ 3
void InitContact(Contact* pc)
{
	assert(pc);
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));//先开辟能存放3个人信息的空间
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;//初始化容量为3
}

5.3 增加联系人函数的优化

刚才我们设计的增加联系人函数首先会判断通讯录的容量是否已满,如果满了就无法添加。现在我们用动态内存来实现后,我们可以再写个函数CheckCapacity来判断通讯录容量,如果没满就无需扩容,如果满了直接扩容即可。

//判断容量函数
#define EXP_SZ 2
int CheckCapacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (EXP_SZ + pc->capacity) * sizeof(PeoInfo));
        //每次扩容2个联系人
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += EXP_SZ;
			printf("增容成功\n");
			return 1;
		}
		else
		{
			perror("CheckCapacity");
			return 0;//扩容失败
		}
	}
	return 1;//不需要增容 
}
//增加联系人函数优化
void AddContact(Contact* pc)
{
	assert(pc);
	if (0 == CheckCapacity(pc))
	{
		return;//扩容失败后无法添加联系人,直接返回
	}

	printf("请输入姓名:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;
	printf("成功添加联系人\n");
}

5.4 释放通讯录空间函数

在使用malloc开辟空间后,需要用free进行释放,那么我们就可以在退出通讯录的时候进行这步操作,我们将其封装在FreeContact函数中实现。

//释放通讯录空间函数
void FreeContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

6 优化后的完整代码

6.1 contact.h

#pragma once
#include 
#include 
#include 
#include 
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TALE 20
#define MAX_ADDR 30
#define DEFAULT_SZ 3
#define EXP_SZ 2
enum OPTION
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW
};
//联系人类型的声明
typedef struct PeoInfo
{
	char name[MAX_NAME];
	size_t age;
	char sex[MAX_SEX];
	char tele[MAX_TALE];
	char addr[MAX_ADDR];
}PeoInfo;//将结构体类型struct PeoInfo重定义为PeoInfo

//通讯录类型的声明
typedef struct Contact
{
	PeoInfo* data;//指向存放数据的空间
	int sz;//用来记录通讯录中的联系人个数
	int capacity;//记录当前通讯录的最大容量
}Contact;//将结构体类型struct Contact重定义为Contact

//函数的声明
void InitContact(Contact* pc);//初始化通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(const Contact* pc);//显示所有联系人信息
void DelContact(Contact* pc);//删除联系人
void SearchContact(const Contact* pc);//查找指定联系人
void ModifyContact(Contact* pc);//修改指定联系人

6.2 contact.c

#include "contact.h"
void InitContact(Contact* pc)
{
	assert(pc);
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));//先开辟能存放3个人信息的空间
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;//初始化容量为3
}

int CheckCapacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (EXP_SZ + pc->capacity) * sizeof(PeoInfo));
        //每次扩容2个联系人
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += EXP_SZ;
			printf("增容成功\n");
			return 1;
		}
		else
		{
			perror("CheckCapacity");
			return 0;//扩容失败
		}
	}
	return 1;//不需要增容 
}
void AddContact(Contact* pc)
{
	assert(pc);
	if (0 == CheckCapacity(pc))
	{
		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);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);

	pc->sz++;//录入成功后,联系人个数+1
	printf("成功添加联系人\n");
}

void ShowContact(const Contact* pc)//由于打印通讯录不需要对其进行修改,因为我们可以加个const进行修饰
{
	assert(pc);
	int i = 0;
	//打印列标题
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	//打印数据
	for (i = 0; i < pc->sz; i++)
	{
		printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

int FindByName(const Contact* pc, char name[])
{
	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)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[MAX_NAME];
	assert(pc);
	//删除
	printf("请输出待删除联系人的姓名:>");
	scanf("%s", name);
	//找到要删除的人
	int i = 0;
	//删除位置在pos的联系人
	int pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要删除的人不存在\n");
		return;
	}
	for (i = pos; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];//将pos后面的位置整体往前移一位
	}
	pc->sz--;
	printf("成功删除联系人\n");
}

void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[MAX_NAME] = { 0 };
	int pos = 0;
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要查找的人不存在\n");
		return;
	}
	printf("%20s\t%4s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%20s\t%4d\t%5s\t%12s\t%30s\n",
		pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tele,
		pc->data[pos].addr);
}

void ModifyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	int pos = 0;
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	pos = FindByName(pc, name);
	if (pos == -1)
	{
		printf("要修改的人不存在\n");
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);

	printf("修改成功\n");
}
	
void FreeContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

6.3 test.c

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

int main()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		menu();//打印菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)//对用户输入的结果进行判断
		{
		case ADD://结果为1,增加联系人
			AddContact(&con);
			break;
		case DEL://结果为2,删除联系人
			DelContact(&con);
			break;
		case SEARCH://结果为3,查找指定联系人
			SearchContact(&con);
			break;
		case MODIFY://结果为4,修改联系人信息
			ModifyContact(&con);
			break;
		case SHOW://结果为5,显示所有联系人信息
			ShowContact(&con);
			break;
		case EXIT://结果为0,退出通讯录
			FreeContact(&con);
			printf("退出通讯录\n");
			break;
		default://所有结果都不是,重新输入
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

效果演示:

通讯录效果演示

你可能感兴趣的:(C语言笔记,c语言,笔记)