29、C语言实现通讯录(含有思路、注释、源码)点赞收藏评论可获得源码

  • Post author:
  • Post category:其他




1 目录结构

  • test.c

    测试

  • contact.c

    实现函数功能

功能:

  1. 存放1000个好友的信息

    • 姓名
    • 电话
    • 性别
    • 住址
    • 年龄
  2. 增加好友信息
  3. 删除指定名字的信息
  4. 查找好友信息
  5. 修改好友信息
  6. 打印好友信息
  7. 排序…



2 静态数组方式



2.1. 测试程序

int main()
{
	int input = 0; 
	//int size = 0; // 记录通讯录有多少个记录

	// 创建通讯录
	// 好友信息结构体存放在 contact.h 中
	Contact con; // 存放1000个人的信息,和size

	// 初始换通讯录
	initContact(&con);

	// 

	do
	{
		menu();
		printf("please choose command number\n");
		scanf("%d", &input);
		switch (input)
		{
		
		case ADD:
			addContactor(&con);
			showContact(&con);
			break;
		case DEL:
			deleteContact(&con);
			showContact(&con);
			break;
		case SEARCH:
			searchContact(&con);
			break;
		case MODIFY:
			modifyContact(&con);
			break;
		case SHOW:
			showContact(&con);
			break;
		case EXIT:
			printf("exit the contact\n");
			break;
		default:
			printf("you have the wrong choose number, please choose again\n");
			break;
		}
	} while (input);
}



2.2 创建的结构体和枚举类

//contact.h

#define MAX 1000
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 50

// 在菜单栏进行选择的时候,如果使用switch case
// case这里跟上1,2,3,4...这些数字,对程序员不友好
// 想要知道数字是哪种操作,还要看一眼菜单对应的操作是什么,不如使用枚举类
enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW
};

// 联系人信息
typedef struct PeoInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;

// 通讯录
typedef struct Contact
{
	int size;  // 记录当前元素个数
	PeoInfo data[MAX]; // 存放1000个信息
}Contact;



2.3. 通讯录初始化

// contact.h
// 初始化通讯录
void initContact(Contact* pcon);

// contact.c
void initContact(Contact* pcon)
{
	memset(pcon->data, 0, sizeof(pcon->data));
	pcon->size = 0; // 设置通讯录最初是 0 个元素
}



2.4. 菜单功能

//test.c

void menu()
{
	printf("********************************************\n");
	printf("*******************通讯录********************\n");
	printf("***********1、add         2、del    *********\n");
	printf("***********3、search      4、modify *********\n");
	printf("***********5、show        0、exit   *********\n");
	printf("********************************************\n");
	printf("********************************************\n");

}



2.5. 添加联系人

// contact.h
// 添加联系人
// 添加失败返回0,添加成功返回1
int addContactor(Contact* pcon);

// contact.c
int addContactor(Contact* pcon)
{
	int size = pcon->size;
	if (size == MAX )
	{
		printf("通讯录已满,添加失败...\n");
		return 0;
	}
	printf("please input the name of your friend\n");
	scanf("%s", pcon->data[size].name);
	printf("please input the tele of your friend\n");
	scanf("%s", pcon->data[size].tele);
	printf("please input the age of your friend\n");
	scanf("%d", &(pcon->data[size].age));
	printf("please input the sex of your friend\n");
	scanf("%s", pcon->data[size].sex);
	printf("please input the addr of your friend\n");
	scanf("%s", pcon->data[size].addr);

	pcon->size = size + 1;
	printf("添加成功...\n");
	return 1;




}



2.6. 查找联系人

// contact.h
// 查找联系人
int searchContact(Contact* con);
// 查找指定姓名的联系人
// 返回下标,未找到返回-1
// 给函数或者全局变量加上 static 时,只能自己所在原文件有效。
static int searchContactByName(Contact* con, char name[]);



//contact.c
static int searchContactByName(Contact* con, char name[]) // 给函数或者全局变量加上 static 时,只能自己所在原文件有效。
{
	for (int i = 0; i < con->size; i++)
	{
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

int searchContact(Contact* con)
{
	int size = con->size;
	char searchName[MAX_NAME] = {0};
	int index = -1;
	if (size == 0)
	{
		printf("the contact  is empty...\n");
		return 0;
	}

	printf("请输入要查找的姓名>\n");
	scanf("%s", searchName);
	index = searchContactByName(con, searchName);
	if (index == -1)
	{
		printf("未查找到姓名为[%s]的联系人\n", searchName);
		return 0;
	}
	printf("该联系人的信息为>\n");
	showContactByIndex(con, index);
	return 1;
}



2.7. 显示全部联系人和显示指定索引处的联系人

// contact.h
// 显示通讯录
void showContact(const Contact* pcon);

// 显示指定索引处联系人的信息
void showContactByIndex(const Contact* pcon, int index);

// contact.c
void showContact(const Contact* pcon)
{

	int size = pcon->size;
	if (size == 0)
	{
		printf("the info of contact is null\n");
		return;
	}
	printf("the info of contact is...\n");
	printf("%-4s\t%-20s\t%-12s\t%-4s\t%-5s\t%-50s \n", "序号", "姓名", "电话", "年龄", "性别", "地址");
	for (int i = 0; i < size; i++)
	{
		
		
		printf("%-4d\t%-20s\t%-12s\t%-4d\t%-5s\t%-50s \n", 
			i, 
			pcon->data[i].name, 
			pcon->data[i].tele, 
			pcon->data[i].age, 
			pcon->data[i].sex,
			pcon->data[i].addr);
	}
	printf("end...\n");
}

void showContactByIndex(const Contact* pcon, int index)
{
	if (index >= pcon->size)
	{
		printf("索引超出范围...\n");
		return;
	}
	
	printf("%-4s\t%-20s\t%-12s\t%-4s\t%-5s\t%-50s \n", "序号", "姓名", "电话", "年龄", "性别", "地址");
	printf("%-4d\t%-20s\t%-12s\t%-4d\t%-5s\t%-50s \n",
		index,
		pcon->data[index].name,
		pcon->data[index].tele,
		pcon->data[index].age,
		pcon->data[index].sex,
		pcon->data[index].addr);
}



2.8. 删除联系人

//contact.h
int deleteContact(Contact* con);

//contact,c
int deleteContact(Contact* con)
{
	int size = con->size;
	int index = -1; // 定义要删除姓名的下标
	char deleteName[MAX_NAME] = {0};
	char delConfirm = 0; // 确认是否删除
	if (size == 0)
	{
		printf("the contact is empty, you can't delete...\n");
		return 0;
	}
	printf("please input the name you need to delete>\n");
	scanf("%s", deleteName);
	index = searchContactByName(con, deleteName);
	if (index == -1)
	{
		printf("未找到姓名为\"%s\"的联系人\n", deleteName);
		return 0;
	}
	
	while (1)
	{
		printf("你确定删除姓名为\"%s\"的联系人吗?y/n\n", deleteName);
		scanf("%c", &delConfirm);
		if (delConfirm == 'y' || delConfirm == 'Y')
		{
			// 确认删除
			
			for (int i = index + 1; i < size; i++)
			{
				con->data[i - 1] = con->data[i];
			}
			con->size--;
			printf("删除成功!\n");
			return 1;
		}
		if (delConfirm == 'n' || delConfirm == 'N')
		{
			// 取消删除
			return 0;
		}
		printf("请输入正确的指令\n");
	}
	return 0;
	
}



2.9. 修改联系人

// contact.h
int modifyContact(Contact* con);


//contact.c
int modifyContact(Contact* con)
{
	char modifyName[MAX_NAME];
	int index = -1;
	if (con->size == 0)
	{
		printf("contact is empty...\n");
		return 0;
	}

	printf("需要修改的联系人姓名:\n");
	scanf("%s", modifyName);
	index = searchContactByName(con, modifyName);
	if (index == -1)
	{
		printf("通讯录没有姓名为【%s】的人\n", modifyName);
		return 0;
	}

	printf("该联系人的信息为>\n");
	showContactByIndex(con, index);
	printf("请输入要修改的信息>\n");
	printf("please input the name of your friend\n");
	scanf("%s", con->data[index].name);
	printf("please input the tele of your friend\n");
	scanf("%s", con->data[index].tele);
	printf("please input the age of your friend\n");
	scanf("%d", &(con->data[index].age));
	printf("please input the sex of your friend\n");
	scanf("%s", con->data[index].sex);
	printf("please input the addr of your friend\n");
	scanf("%s", con->data[index].addr);
	printf("***********修改成功***********\n");
	printf("修改后的联系人信息为>\n");
	showContactByIndex(con, index);
	return 1;
	
}



3 动态申请内存方式

上面写的通讯录,上来就能存放1000个人的信息,如果多了少了对内存都不友好。

那我们就可以设计,比如默认可以存放10个人的信息,当发现通讯录满的时候,我们进行动态的扩容。每次增加5个人的空间等等:

10 15 20 25...



3.1. 修改通讯录结构体

typedef struct Contact
{
	//Info data[MAX];
	Info* data;
	int size;
    int capacity; // 通讯录的最大容量,当size和这个一样大时,我们进行扩容
}Contact;

我们给通讯录初始化时,是创建一个有10个人信息的空间,当容量达到10个时,我们进行扩容。


所以,除了需要记录通讯录当前包含联系人的个数之外,还要记录当前通讯录的容量,这一点在静态数组中没有记录的原因,是静态数组已经规定了最大容量,这个值我们是已知的



3.2. 初始化通讯录

初始化时,我们可以默认是10,也可以默认是1,2,3,4,5等等任意的数字,所以我们可以在

contact.h

中定义


#define DEFALUT_CAPACITY 10

void initContact(Contact* pcon)
{
	//memset(pcon->data, 0, sizeof(pcon->data));
	pcon->data = (PeoInfo*)malloc(DEFAULT_CAPACITY * sizeof(PeoInfo));
	if (pcon->data == NULL)
	{
		return;
	}
	

	pcon->size = 0; // 设置通讯录最初是 0 个元素
	
	pcon->capacity = DEFAULT_CAPACITY;

}



3.3. 增加联系人

通讯录满的时候,原来是不能增加,现在是扩容


仍然在contact.h中定义默认的扩容个数


#define EXTEND_CAPACITY 5

int addContactor(Contact* pcon)
{
	int size = pcon->size;
	// 判断通讯录容量是否满了
	// 如果满了,就扩容,扩容完继续执行添加
	// 如果不满,就继续添加
	if (contactIsFull(pcon))
	{
		// 扩容
		PeoInfo* ptemp = (PeoInfo*)realloc(pcon->data, (pcon->capacity + EXTEND_CAPACITY) * sizeof(PeoInfo));
		if (ptemp == NULL)
		{
			printf("添加失败\n");
			return 0;
		}
		pcon->data = ptemp;
		pcon->capacity += 2;
		printf("扩容成功\n");
		return 1;
	}

	printf("please input the name of your friend\n");
	scanf("%s", pcon->data[size].name);
	printf("please input the tele of your friend\n");
	scanf("%s", pcon->data[size].tele);
	printf("please input the age of your friend\n");
	scanf("%d", &(pcon->data[size].age));
	printf("please input the sex of your friend\n");
	scanf("%s", pcon->data[size].sex);
	printf("please input the addr of your friend\n");
	scanf("%s", pcon->data[size].addr);
	
	pcon->size = size + 1;
	printf("添加成功...\n");
	return 1;


}

// 这里面涉及到一个contactIsFull函数,是用来判断当前通讯录是否为满的
// 声明在 contact.h中
static int contactIsFull(const Contact* pcon);

// 定义在contact.c
static int contactIsFull(const Contact* pcon)
{
	if (pcon->size == pcon->capacity)
	{
		return 1;
	}
	return 0;
}



3.4. 删除/修改/查找联系人

不需要修改,因为扩容完了之后,再删除,只是移动 size 这个标记,并把原来的内容往前移动,并不涉及内存的回收。



3.5. 退出(销毁内存空间)

销毁通讯录,释放动态申请的内存空间

//contact.h
// 释放内存空间
void destroyContact(Contact* pcon);


//contact.c
// 释放内存空间
void destroyContact(Contact* pcon)
{
	free(pcon->data);
	pcon->data = NULL;
}



4 文件版本的通讯录

之前的版本,是都放在内存中的,当程序结束运行时,通讯录就会销毁了。

我们现在把通讯录持久化到文件中去。

我们可以新增一个保存

save

函数。



4.1. 菜单更改,添加一个save

void menu()
{
	printf("********************************************\n");
	printf("*******************通讯录********************\n");
	printf("***********1、add         2、del    *********\n");
	printf("***********3、search      4、modify *********\n");
	printf("***********5、show        6、save   *********\n");
	printf("***********        0、xeit          *********\n");
	printf("********************************************\n");

}

所以在枚举类和测试函数的switch case中,也要加上

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,

	SAVE
};



4.2. 添加保存到文件的操作

case SAVE:
			saveContact(&con);
			break;
case EXIT:
			// 销毁通讯录,释放动态申请的内存空间
// 退出销毁之前,最好是也要保存一下通讯录
			saveContact(&con);
			destroyContact(&con);
			printf("exit the contact\n");
			break;
// contact.h
// 保存文件
void saveContact(Contact* pcon);

// contact.c
// 保存文件
void saveContact(Contact* pcon)
{
	FILE* pfWrite = fopen("contact.txt", "wb");
	if (pfWrite == NULL)
	{
		printf("saveContact::%s\n", strerror(errno));
	}
	if (pcon->size == 0)
	{
		printf("通讯录为空...\n");
		return;
	}
	//fprintf(pfWrite,"%-4s\t%-20s\t%-12s\t%-4s\t%-5s\t%-50s \n", "序号", "姓名", "电话", "年龄", "性别", "地址");

	for (int i = 0; i < pcon->size; i++)
	{
		fwrite(&(pcon->data[i]), sizeof(PeoInfo), 1, pfWrite);
	}
	//fprintf(pfWrite ,"end...\n");

	fclose(pfWrite);
	pfWrite = NULL;
}



4.3. 初始化通讯录



初始化通讯录时,我们最好是把保存在文件中的数据,读入到内存中,这样才更合理

void initContact(Contact* pcon)
{
	
	//memset(pcon->data, 0, sizeof(pcon->data));
	pcon->data = (PeoInfo*)malloc(DEFAULT_CAPACITY * sizeof(PeoInfo));
	if (pcon->data == NULL)
	{
		return;
	}


	pcon->size = 0; // 设置通讯录最初是 0 个元素

	pcon->capacity = DEFAULT_CAPACITY;

	// 把文件加载到通讯录中
	loadContact(pcon);

}

// 其中 loadContact 在 contact.h中声明
// 在 contact.c 中定义
// 加载文件中的信息到通讯录中
void loadContact(Contact* pcon)
{
	FILE* pfRead = fopen("contact.txt", "rb");
	PeoInfo tmp = {0};
	if (pfRead == NULL)
	{
		printf("loadContact::%s\n", strerror(errno));
		return;
	}
	
	while (fread(&tmp,sizeof(PeoInfo),1,pfRead)) // 读取的返回值,是实际读取到的元素个数,如果没有读到,就说明是返回0
	{
		pcon->data[pcon->size] = tmp;
		pcon->size++;
		if (contactIsFull(pcon))
		{
			// 扩容
			PeoInfo* ptemp = (PeoInfo*)realloc(pcon->data, (pcon->capacity + EXTEND_CAPACITY) * sizeof(PeoInfo));
			if (ptemp == NULL)
			{
				printf("添加失败\n");
				return 0;
			}
			pcon->data = ptemp;
			pcon->capacity += 2;
			//printf("扩容成功\n");

		}
	}
	


	// 关闭文件
	fclose(pfRead);
	pfRead = NULL;
}




版权声明:本文为xjtuxinghy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。