C语言——通讯录的简单实现

本文详细介绍了如何实现一个简单的通讯录应用,包括添加、删除、查找、修改联系人,以及按姓名、性别和年龄排序的功能。通过实际代码展示了关键步骤和辅助函数的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为和前面的小游戏实现方式是一个套路,所以分模块(声明模块、函数实现模块、测试模块)部分不再重复,感兴趣请点击->三子棋小游戏实现

下面开始这次项目的实现:

一、大概流程框架实现

老规矩!首先是要把大的运行框架做出来,这里直接把测试和声明的代码放在一个内容。(为了方便讲解,说明部分都放到了注释中)

1、声明(contact.h)

//实现用到的库函数
#include <stdio.h>
#include <string.h>

//联系人的一些属性,需要给定一定大小的空间,这里使用#define定义的常量,方便后面使用
#define NAME 8
#define SEX 5
#define PHONE 12
#define ADDR 20
#define NUM 100

//定义的每个联系人结构体,包括五个属性(姓名,性别,年龄,电话,地址)
//定义完之后通过typedef重定义为people,方便后面调用
typedef struct people
{
	char name[NAME];
	char sex[SEX];
	int age;
	char phone[PHONE];
	char addr[ADDR];
}people;

//定义的所有联系人(即通讯录)结构体,包括两个属性(联系人,数量)
//定义完之后通过typedef重定义为contact,方便后面调用
typedef struct contact
{
	people data[NUM];
	int num;
}contact;

//该项目实现的具体功能
//包括对联系人的增加、删除、查找、修改、显示所有联系人、排序、清空联系人列表
void add(contact* c);
void show(contact* c);
void del(contact* c);
void search(contact* c);
void modify(contact* c);
void clear(contact* c);
void sort(contact* c);

2、测试(test.c)

#define _CRT_SECURE_NO_WARNINGS

//只要包含自定义头文件就可以使用所有东西啦
#include "contact.h"

//这里定义一个menu函数,方便用户做选择
void menu()
{
	printf(" ___________________________________\n");
	printf("|                                   |\n");
	printf("|     1.add     2.del   3.search    |\n");
	printf("|     4.modify  5.sort  6.show      |\n");
	printf("|          7.claer   0.exit         |\n");
	printf("|___________________________________|\n");
	printf("\n");
	return;
}

//因为到这里已经基本掌握了枚举常量
//另外定义枚举常量之后,也增加了代码可读性,所以最好用一下,嘻嘻
enum menu
{
	ADD = 1,
	DEL = 2,
	SEARCH = 3,
	MODIFY = 4,
	SORT = 5,
	SHOW = 6,
	CLEAR = 7,
	EXIT = 0
};

int main()
{
    //对需要用到的变量进行定义和初始化,这里contact是前面重定义之后结构体
	int input = 0;
	contact c = { 0 };
	do
	{
		menu();
		printf("请输入你的选择:");
		scanf("%d", &input);
		printf("\n");
		//通过switch语句实现选择具体功能的逻辑
		switch (input)
		{
		case ADD:
			add(&c);
			break;
		case DEL:
			del(&c);
			break;
		case SEARCH:
			search(&c);
			break;
		case MODIFY:
			modify(&c);
			break;
		case SORT:
			sort(&c);
			break;
		case SHOW:
			show(&c);
			break;
		case CLEAR:
			clear(&c);
			break;
		case EXIT:
			printf("感谢使用,已退出!\n");
			break;
		default:
			printf("输入错误,请输入正确的数字!\n");
		}
	} 	
	while (input);
	return 0;
}

到这里大框架基本就完成了,只要再完成具体功能的实现就可以使用了。

二、具体功能模块的实现(contact.c)

1、辅助函数——通过姓名找到对应联系人数组下标

因为这个函数只在该源文件下使用,所以我把它定义成了静态函数,限定了其作用域,同时增加安全性。至于查找过程没什么难度,也就不再赘述。

static int findByName(contact* c, char* name)
{
	int i = 0;
	for (i = 0; i < c->num; i++)
	{
		if (strcmp(c->data[i].name, name) == 0)
			return i;
	}
	return -1;
}

2、显示通讯录

void show(contact* c)
{
	printf("%-NAMEs %-SEXs %-AGEs %-PHONEs %-ADDRs\n","姓名","性别","年龄","电话","地址");
	int i = 0;
	for (i = 0; i < c->num; i++)
	{
		printf("%-NAMEs %-SEXs %-AGEd %-PHONEs %-ADDRs\n", c->data[i].name, c->data[i].sex, c->data[i].age,
			                                   c->data[i].phone, c->data[i].addr);
	}
	printf("显示完毕!\n");
	return;
}

3、增加联系人

void add(contact* c)
{
	printf("请输入需要添加的联系人信息(姓名,性别,年龄,电话,地址):\n");
	scanf("%s %s %d %s %s", c->data[c->num].name, c->data[c->num].sex, &(c->data[c->num].age),
		                    c->data[c->num].phone, c->data[c->num].addr);
	c->num++;
	printf("添加成功!\n");
	return;
}

这里只说一点,增加完联系人之后不能忘记给联系人数组中的联系人数量num加1。

4、删除联系人

这里就用到了前面的辅助函数,先根据名字找到人,通过覆盖把要删除的联系人覆盖掉,再给联系人数组中的联系人数量num减一。

void del(contact* c)
{
	if (c->num == 0)
	{
		printf("您的通讯录现在还没有联系人,请先添加喔!\n");
	}
	else
	{
		printf("请输入需要删除的联系人姓名:");
		char name[NAME];
		scanf("%s", name);
		int ret = findByName(c, name);
		printf("\n");
		if (ret == -1)
		{
			printf("对不起,查无此人呢!\n");
		}
		else
		{
			int i = 0;
			for (i = ret; i < c->num - 1; i++)
			{
				c->data[i] = c->data[i + 1];
			}
			c->num--;
			printf("删除完毕!\n");
		}
	}
	return;
}

5、查找联系人

这部分其实没有做什么实现,直接调用辅助函数,再加一下提示就好了。

void search(contact* c)
{
	if (c->num == 0)
	{
		printf("您的通讯录现在还没有联系人,请先添加喔!\n");
	}
	else
	{
		printf("请输入需要查找的联系人姓名:");
		char name[NAME];
		scanf("%s", name);
		int ret = findByName(c, name);
		printf("\n");
		if (ret == -1)
		{
			printf("对不起,查无此人呢!\n");
		}
		else
		{
			printf("哇哦!找到了!\n");
			printf("\n");
			printf("%%-NAMEs %-SEXs %-AGEs %-PHONEs %-ADDRs\n", "姓名", "性别", "年龄", "电话", "地址");
			printf("%-NAMEs %-SEXs %-AGEd %-PHONEs %-ADDRs\n", c->data[ret].name, c->data[ret].sex, c->data[ret].age,
				                                   c->data[ret].phone, c->data[ret].addr);
		}
	}
	return;
}

6、修改联系人

也是借助辅助函数,找到对应联系人之后,把修改后的信息重新赋给它,直接覆盖就好。

void modify(contact* c)
{
	if (c->num == 0)
	{
		printf("您的通讯录现在还没有联系人,请先添加喔!\n");
	}
	else
	{
		printf("请输入需要修改的联系人姓名:");
		char name[NAME];
		scanf("%s", name);
		int ret = findByName(c, name);
		printf("\n");
		if (ret == -1)
		{
			printf("对不起,查无此人呢!\n");
		}
		else
		{
			printf("请输入修改后的联系人信息(姓名,性别,年龄,电话,地址):\n");
			scanf("%s %s %d %s %s", c->data[ret].name, c->data[ret].sex,&( c->data[ret].age),
				                      c->data[ret].phone, c->data[ret].addr);
			printf("修改成功!\n");
		}
	}
	return;
}

7、清空联系人

这部分直接调用一个memset把结构体数组置 0 ,编译器会自动识别为结束标识符,就可以完成通讯录的清空。

void clear(contact* c)
{
	memset(c, 0, sizeof(*c));//c是指针,不是数组
	printf("清空完毕!\n");
	return;
}

8、对联系人排序

因为排序方式有很多,在这个通讯录中可以通过姓名、性别、年龄排序,所以封装了3个静态函数分别实现这个功能,再通过总的排序函数调用。需要注意的是,要把这三个静态函数放到总排序函数前,要不然就要做好声明,因为这三个静态函数是只作用在该源文件内部,只有放到调用之前才可以不做声明。

排序是采用的升序的冒泡排序。众所周知,冒泡排序是双重for循环去实现的,外面一层表示要排序的趟数,里面一层把每趟的最大值放到最后,若有不符合直接交换位置。这里可以做一个小小的优化,设立一个岗哨 flag ,若是有一趟全部都没有交换位置,表示已经是升序了,这时直接跳出循环,排序完毕!

(1)排序总函数

void sort(contact* c)
{
	printf("请选择排序方式对应的数字(1.姓名/2.性别/3.年龄):");
	int sortnum = 0;
	scanf("%d", &sortnum);
	switch (sortnum)
	{
	case 1:
		sortByName(c);
		break;
	case 2:
		sortBySex(c);
		break;
	case 3:
		sortByAge(c);
		break;
	default:
		printf("请输入规定数字:\n");
		break;
	}
	return;
}

(2)通过“姓名”排序

static void sortByName(contact* c)
{
	int i = 0;
	for (i = 0; i < c->num - 1; i++)
	{
		int flag = 0;
		int j = 0;
		for (j = 0; j < c->num - 1 - i; j++)
		{
			if (strcmp(c->data[j].name, c->data[j + 1].name) > 0)
			{
				people tmp = c->data[j];
				c->data[j] = c->data[j + 1];
				c->data[j + 1] = tmp;
				flag = 1;
			}
		}
		if (flag == 0)
		{
			break;
		}
	}
	printf("按名字排序完毕!\n");
	return;
}

(3)通过“性别”排序

static void sortBySex(contact* c)
{
	int i = 0;
	for (i = 0; i < c->num - 1; i++)
	{
		int flag = 0;
		int j = 0;
		for (j = 0; j < c->num - 1 - i; j++)
		{
			if (strcmp(c->data[i].sex, c->data[i + 1].sex) > 0)
			{
				people tmp = c->data[i];
				c->data[i] = c->data[i + 1];
				c->data[i + 1] = tmp;
				flag = 1;
			}
		}
		if (flag == 0)
		{
			break;
		}
	}
	printf("按性别排序完毕!\n");
	return;
}

(4)通过“年龄”排序

static void sortByAge(contact* c)
{
	int i = 0;
	for (i = 0; i < c->num - 1; i++)
	{
		int flag = 0;
		int j = 0;
		for (j = 0; j < c->num - 1 - i; j++)
		{
			if (c->data[i].age > c->data[i + 1].age)
			{
				people tmp = c->data[i];
				c->data[i] = c->data[i + 1];
				c->data[i + 1] = tmp;
				flag = 1;
			}
		}
		if (flag == 0)
		{
			break;
		}
	}
	printf("按年龄排序完毕!\n");
	return;
}

三、总结收获

这已经是写的第三个跨文件代码,感觉已经基本摸清了这类程序的套路。这次的实现相较前两个简单很多,没有很复杂的逻辑,当然功能也很单一薄弱。没写之前觉得很难,写了之后就觉得很多东西其实没有想象的那么难,前提是你要行动!!!哈哈哈,强行鸡汤,反正是我的真实感受,这次分享这就结束了~拜拜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值