C语言通讯录功能的实现

C/C++
157
0
0
2024-06-20

C语言通讯录功能的实现

一、项目需求:

我们需要一个通讯录,可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址。

并且在通讯录中要求实现下述功能:

  1. 添加联系人信息
  2. 删除指定联系人信息
  3. 查找指定联系人信息
  4. 修改指定联系人信息
  5. 显示所有联系人信息
  6. 以名字排序所有联系人

二、模块化代码

在代码编辑中,为了避免代码过于冗长,也为了便于后续功能的修改。我们一般将代码分多成多个模块。

建立如下文件

contact.h (头文件,用来引入库函数,存放声明) contact.c (实现通讯录每个部分的功能) test.c (主函数,用于测试通讯录功能是否成功实现)

注意:.c文件需要使用自定义头文件的声明,要在最前端引入

#include "contact.h"		//自定义头文件需要用"" 而不是<>

三、整体思路

1.创建结构体类型

我们需要一个通讯录,可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址。

这里我们就需要建立一个结构体,描述这信息。

#define nameMax  15
#define sexMax 5
#define numberMax 15
#define addrMax 20

//结构体定义,表示通讯录的每一页,包含了一个联系人的所有信息
typedef struct inform
{
	char name[nameMax];
	char sex[sexMax];
	int age;
	char number[numberMax];
	char addr[addrMax];

}inform;

这里我使用了typedef关键字关于typedef:

typedef 是类型重命名 与struct 结构体 连起来使用,可以让struct 在定义完成后使用{}后的名字作为类型名。

一个人的通讯录信息结构体已经已经建立好了。现在我们需要一个结构体数组,来存放1000个人的信息,同时出于功能的需求,我们还要知道通讯录已经存储了多少个人的信息,所以我们再建立一个通讯录本结构体。

//定义整本通讯录,能够存储100个人的信息和其通讯录所在位置
typedef struct Contact
{
	inform date[1000];
	int num;
}Contact;
这里使用了结构体嵌套,结构体内部元素是一个结构体数组;
2.建立循环

首先我们要有一个菜单,向使用者展示通讯录的功能,并且提示他们可以输入对应的字符来进行相对应的功能。

并且这是一个循环,可以不断输入来选择功能。

void meanu()
{
	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");
}
int main()
{
	int input = 0;
	do 
	{
		meanu();
		switch (input)
		{
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			break;
		case 6:
			break;
		case 7:
			break;
		case 0:
			break;
		default:
			break;
		}
	} while (input);
	return 0;
}

建立了菜单循环,下面就要开始往每一个选择加入不同的功能。

但是我们在编写程序1,2,3,4…这样的数字,经常需要回头查看设置的功能是什么

我们可以使用枚举常量

关于枚举常量 枚举常量有点类似于宏定义#define,是将一系列名字定义为常量。其默认赋值顺序是从0开始,逐个+1 缺点:只能赋值为整形常量。
enum
{
	Exit,
	Add,
	Del,
	Search,
	Modify,
	Show,
	Sort,
};

写在contact.h头文件中

那么循环可以更改为上述名字。

我们在将exit功能和default功能实现一下。

#define _CRT_SECURE_NO_WARNINGS
#include "contact.h"

void meanu()
{
	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");
}
int main()
{
	int input = 0;
	do 
	{
		meanu();
		scanf("%d", &input);
		switch (input)
		{

		case Add:
			break;
		case Del:
			break;
		case Search:
			break;
		case Modify:
			break;
		case Show:
			break;
		case Sort:
			break;
		case Exit:
			printf("退出成功!!!\n");
			break;
		default:
			printf("您输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

注意!!!

初始化结构体之后,结构体的初始值 !=0,需要主动初始化(赋予空白内容)

并且结构体的函数传递必须为 传址传递 否则无法修改结构体内部的信息。

//结构体初始化函数
void initContact(Contact* p)
{
	//先判断穿过来的指针是否为空
	assert(p);
	p->num = 0;
	memsent(p->date,0,sizeof(p->date));
}
利用memsent函数 可以快速的将date数组里的内容进行初始化
3.各个功能的实现
(1)add()增添联系人


//新增联系人
void add(Contact* p)
{
	assert(p);
	//判断通讯录是否已经未满,如果num = 100则无法添加
	if (1000 == p->num)
	{
		printf("通讯录已满,无法进行添加\n");
		return ;
	}

	printf("请输入姓名:\n");
	scanf("%s",p->date[p->num].name);
	printf("请输入性别:\n");
	scanf("%s",p->date[p->num].sex);
	printf("请输入年龄:\n");
	scanf("%d",&(p->date[p->num].age));
	printf("请输入号码:\n");
	scanf("%s",p->date[p->num].number);
	printf("请输入地址:\n");
	scanf("%s",p->date[p->num].addr);
	p->num += 1;
	printf("联系人信息已添加完成!\n");
}

注意:

在新增联系人的时候,我们首先需要判断通讯录是否为满。如果为满则跳出并给出提示。

(2)show()显示通讯录信息


void show(Contact* p)
{
	assert(p);
	if (0 == p->num)
	{
		printf("没有可以显示的信息!\n");
		return ;
	}
	printf("%-15s %-5s %-5s %-15s %-20s\n","姓名","性别","年龄","电话","地址");
	int i = 0;

	for (i = 0; i < p->num; i++)
	{
		printf("%-15s %-5s %-5d %-15s %-20s\n",
			p->date[i].name, p->date[i].sex, p->date[i].age, p->date[i].number, p->date[i].addr);
	}
}

如果结构体都为0,则不需要打印。

(3)search查找通讯录信息

这里我们考虑的是按人名进行查找,由于后面的删除功能也需要进行按人名进行查找,所以先自定义一个按名索引函数

按名索引 让用户输入要进行操作的联系人的名字,通过strcmp函数比较是否相同 如果相同 return 其所在位置 如果不同 return -1
//构建一个内部函数,用于实现按名查找
int findName(Contact* p , char name[])
{
	assert(p);
	int i = 0;
	//遍历通讯录,找到了返回是第几个联系人
	for (i = 0; i < p->num; i++)
	{
		if (0 == strcmp(p->date[i].name, name))
			return i;
	}
	//如果找不到返回 -1
	return -1;
}

由于这个函数仅供该文件下的函数调用,所以不需要在头文件中声明也是可以的。

有了按名索引功能,搜索功能就简单了许多:

但是仍有需要注意的是:

  • 首先通讯里要保证有内容可查,如果没有则进行提示
  • 查找到后要进行打印(同show函数的功能)
//查找联系人的函数
void search(Contact* p)
{
	assert(p);
	char name[nameMax] ;
	if (p->num == 0)
	{
		printf("通讯录无数据可供查找!\n");
		printf("请从新进行操作!\n");
		return;
	}
	printf("请输入你要查找人的姓名:");
	scanf("%s",name);
	//rer用来接收findName的返回值,即所查人所在的位置
	int pos = findName(p, name);

	//如果返回值为 -1 表示
	if (-1 == pos)
	{
		printf("没有找到这个人的信息!\n");
		return;
	}
	printf("查找成功:\n");
	//显示查找的人的信息:
	printf("%-15s %-5s %-5s %-15s %-20s\n",
		"姓名", "性别", "年龄", "电话", "地址");
	printf("%-15s %-5s %-5d %-15s %-20s\n",
		p->date[pos].name, 
		p->date[pos].sex,
		p->date[pos].age,
		p->date[pos].number,
		p->date[pos].addr);
}
(4)del删除通讯录信息功能

删除通讯录是建立在查找功能之上的,先对输入的人名进行按名索引,随换把要删除的信息进行覆盖,让后面的每一个元素都向前移动1位

计算机信息删除的本质,其实也就是把不可修改的区域进行覆盖,使其变为可修改的区域。

代码如下:

//删除联系人
void del(Contact* p)
{
	assert(p);
	char name[nameMax];

	if (p->num == 0)
	{
		printf("通讯录为空!!!\n");
		printf("请从新进行操作!\n");
		return;
	}

	printf("请输入你要删除人的姓名:");
	scanf("%s", name);
	//rer用来接收findName的返回值,即所查人所在的位置
	int pos = findName(p, name);

	//如果返回值为 -1 表示
	if (-1 == pos)
	{
		printf("没有找到这个人的信息!\n");
		return;
	}
	
	//删除
	int i = 0;
	for (i = pos; i < p->num; i++)	
	{
		p->date[i] = p->date[i + 1];	//同类型之间的变量可以进行赋值操作
	}
	p->num--;							//防止数组越界
	printf("清楚完毕!!\n");
}
(5)modify修改信息功能
//对通讯录信息进行修改
void modify(Contact* p)
{
	assert(p);
	char name[nameMax];

	if (p->num == 0)
	{
		printf("通讯录为空!!!\n");
		printf("请从新进行操作!\n");
		return;
	}

	printf("请输入你要修改人的姓名:");
	scanf("%s", name);
	//rer用来接收findName的返回值,即所查人所在的位置
	int pos = findName(p, name);

	//如果返回值为 -1 表示
	if (-1 == pos)
	{
		printf("没有找到这个人的信息!\n");
		return;
	}

	//修改:
	int choose = 0;
	do 
	{
		//选择修改菜单:
		printf("………………………………………………\n");
		printf("***   1.姓名        2.性别       ***\n");
		printf("***   3.年龄        4.号码       ***\n");
		printf("***   5.地址                     ***\n");
		printf("***   0.退出修改                 ***\n");
		printf("………………………………………………\n");

		printf("请输入你要修改的选项:>");
		scanf("%d", &choose);

		switch (choose)
		{
		case 1:
			printf("请输入修改后的姓名:");
			scanf("%s",p->date[pos].name);
			break;
		case 2:
			printf("请输入修改后的性别:");
			scanf("%s",p->date[pos].sex);
			break;
		case 3:
			printf("请输入修改后的年龄:");
			scanf("%d",p->date[pos].age);
			break;
		case 4:
			printf("请输入修改后的号码:");
			scanf("%s",p->date[pos].number);
			break;
		case 5:
			printf("请输入修改后的地址:");
			scanf("%s",p->date[pos].addr);
			break;
		case 0:
			printf("修改成功!!!\n");
			break;
		default :
			printf("非法字符输入,请重新选择\n");
			break;
		}

	} while (choose);
	

}

前面的功能基本一直,按名索引查找被操作人所在位置

后面类比主函数功能,建立一个修改信息菜单。直接对通讯录里的信息修改

(6)sort函数排序
//sort排序功能(按照名字排升序)
void sort(Contact* p)
{
	assert(p);
	Contact temp;
	if (p->num == 0)
		printf("通讯录为空,无法排序!!\n");
	//冒泡排序
	int i = 0;
	int j = 0;
	for (i = 0; i < p->num; i++)
	{
		for (j = 0; j < p->num - i - 1; j++)
		{
			if (strcmp(p->date[j].name, p->date[j + 1].name) > 0)
			{
				temp.date[0] = p->date[i];
				p->date[i] = p->date[i + 1];
				p->date[i + 1] = temp.date[i];
			}

		}
	}
}

排序的选择有很多种,可以按年龄排序,也可以自己进行选择

四、代码汇总

contact.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>

#define nameMax  15
#define sexMax 5
#define numberMax 15
#define addrMax 20

//结构体定义,表示通讯录的每一页,包含了一个联系人的所有信息
typedef struct inform
{
	char name[nameMax];
	char sex[sexMax];
	int age;
	char number[numberMax];
	char addr[addrMax];

}inform;

//定义整本通讯录,能够存储100个人的信息和其通讯录所在位置
typedef struct Contact
{
	inform date[1000];
	int num;
}Contact;

enum
{
	Exit,
	Add,
	Del,
	Search,
	Modify,
	Show,
	Sort,
};

//初始化结构体
void initContact(Contact* p);


//加入联系人
void add(Contact* p);


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


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

//查找联系人
void search(Contact* p);

//对通讯录信息进行修改
void modify(Contact* p);

//sort排序功能
void sort(Contact* p);
contact.c
#include "contact.h"


void initContact(Contact* p)
{
	//先判断穿过来的指针是否为空
	assert(p);
	p->num = 0;
	memset(p->date,0,sizeof(p->date));
}






//新增联系人
void add(Contact* p)
{
	assert(p);
	//判断通讯录是否已经未满,如果num = 100则无法添加
	if (1000 == p->num)
	{
		printf("通讯录已满,无法进行添加\n");
		return ;
	}

	printf("请输入姓名:\n");
	scanf("%s",p->date[p->num].name);
	printf("请输入性别:\n");
	scanf("%s",p->date[p->num].sex);
	printf("请输入年龄:\n");
	scanf("%d",&(p->date[p->num].age));
	printf("请输入号码:\n");
	scanf("%s",p->date[p->num].number);
	printf("请输入地址:\n");
	scanf("%s",p->date[p->num].addr);
	p->num += 1;
	printf("联系人信息已添加完成!\n");

}






//展示通讯录信息
void show(Contact* p)
{
	assert(p);
	if (0 == p->num)
	{
		printf("没有可以显示的信息!\n");
		return ;
	}
	printf("%-15s %-5s %-5s %-15s %-20s\n",
		"姓名","性别","年龄","电话","地址");
	int i = 0;

	for (i = 0; i < p->num; i++)
	{
		printf("%-15s %-5s %-5d %-15s %-20s\n",
			p->date[i].name,
			p->date[i].sex,
			p->date[i].age,
			p->date[i].number,
			p->date[i].addr);
	}

}









//构建一个内部函数,用于实现按名查找
int findName(Contact* p , char name[])
{
	assert(p);
	int i = 0;
	//遍历通讯录,找到了返回是第几个联系人
	for (i = 0; i < p->num; i++)
	{
		if (0 == strcmp(p->date[i].name, name))
			return i;
	}
	//如果找不到返回 -1
	return -1;

}








//查找联系人的函数
void search(Contact* p)
{
	assert(p);
	char name[nameMax] ;
	if (p->num == 0)
	{
		printf("通讯录无数据可供查找!\n");
		printf("请从新进行操作!\n");
		return;
	}
	printf("请输入你要查找人的姓名:");
	scanf("%s",name);
	//rer用来接收findName的返回值,即所查人所在的位置
	int pos = findName(p, name);

	//如果返回值为 -1 表示
	if (-1 == pos)
	{
		printf("没有找到这个人的信息!\n");
		return;
	}
	printf("查找成功:\n");
	//显示查找的人的信息:
	printf("%-15s %-5s %-5s %-15s %-20s\n",
		"姓名", "性别", "年龄", "电话", "地址");
	printf("%-15s %-5s %-5d %-15s %-20s\n",
		p->date[pos].name, 
		p->date[pos].sex,
		p->date[pos].age,
		p->date[pos].number,
		p->date[pos].addr);
}









//删除联系人
void del(Contact* p)
{
	assert(p);
	char name[nameMax];

	if (p->num == 0)
	{
		printf("通讯录为空!!!\n");
		printf("请从新进行操作!\n");
		return;
	}

	printf("请输入你要删除人的姓名:");
	scanf("%s", name);
	//rer用来接收findName的返回值,即所查人所在的位置
	int pos = findName(p, name);

	//如果返回值为 -1 表示
	if (-1 == pos)
	{
		printf("没有找到这个人的信息!\n");
		return;
	}
	
	//删除
	int i = 0;
	for (i = pos; i < p->num; i++)	
	{
		p->date[i] = p->date[i + 1];	//同类型之间的变量可以进行赋值操作
	}
	p->num--;							//防止数组越界
	printf("清楚完毕!!\n");
}







//对通讯录信息进行修改
void modify(Contact* p)
{
	assert(p);
	char name[nameMax];

	if (p->num == 0)
	{
		printf("通讯录为空!!!\n");
		printf("请从新进行操作!\n");
		return;
	}

	printf("请输入你要修改人的姓名:");
	scanf("%s", name);
	//rer用来接收findName的返回值,即所查人所在的位置
	int pos = findName(p, name);

	//如果返回值为 -1 表示
	if (-1 == pos)
	{
		printf("没有找到这个人的信息!\n");
		return;
	}

	//修改:
	int choose = 0;
	do 
	{
		//选择修改菜单:
		printf("………………………………………………\n");
		printf("***   1.姓名        2.性别       ***\n");
		printf("***   3.年龄        4.号码       ***\n");
		printf("***   5.地址                     ***\n");
		printf("***   0.退出修改                 ***\n");
		printf("………………………………………………\n");

		printf("请输入你要修改的选项:>");
		scanf("%d", &choose);

		switch (choose)
		{
		case 1:
			printf("请输入修改后的姓名:");
			scanf("%s",p->date[pos].name);
			break;
		case 2:
			printf("请输入修改后的性别:");
			scanf("%s",p->date[pos].sex);
			break;
		case 3:
			printf("请输入修改后的年龄:");
			scanf("%d",p->date[pos].age);
			break;
		case 4:
			printf("请输入修改后的号码:");
			scanf("%s",p->date[pos].number);
			break;
		case 5:
			printf("请输入修改后的地址:");
			scanf("%s",p->date[pos].addr);
			break;
		case 0:
			printf("修改成功!!!\n");
			break;
		default :
			printf("非法字符输入,请重新选择\n");
			break;
		}

	} while (choose);
	

}





//sort排序功能(按照名字排升序)
void sort(Contact* p)
{
	assert(p);
	Contact temp;
	if (p->num == 0)
		printf("通讯录为空,无法排序!!\n");
	//冒泡排序
	int i = 0;
	int j = 0;
	for (i = 0; i < p->num; i++)
	{
		for (j = 0; j < p->num - i - 1; j++)
		{
			if (strcmp(p->date[j].name, p->date[j + 1].name) > 0)
			{
				temp.date[0] = p->date[i];
				p->date[i] = p->date[i + 1];
				p->date[i + 1] = temp.date[i];
			}

		}
	}
}
test.c
#include "contact.h"

void meanu()
{
	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");
}
int main()
{
	int input = 0;
	//创建通讯录
	Contact contact;
	//注意:结构体类型创建的时候初始值是一个随机值,不是0,
	//需要对结构体进行初始化
	initContact(&contact);

	do 
	{
		meanu();
		scanf("%d", &input);
		switch (input)
		{

		case Add:
			add(&contact);
			break;
		case Del:
			del(&contact);
			break;
		case Search:
			search(&contact);
			break;
		case Modify:
			modify(&contact);
			break;
		case Show:
			show(&contact);
			break;
		case Sort:
			break;
		case Exit:
			printf("退出成功!!!\n");
			break;
		default:
			printf("您输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

五、反思和总结

这里只是一个简单的C语言项目,里面还有很多可以改进的地方。 如:

  • 排序函数是否可以用qsort函数?
  • 通讯录结构体是否可以使用动态内存管理?
  • 如何添加文件功能? 这些都是值得去进一步学习和改进的地方。