C语言——通讯录管理系统

本文介绍了通讯录管理系统项目,包括多文件代码组织、结构体与数组的使用、菜单操作(添加、显示、删除、查找、修改和清空)以及代码优化建议,如使用函数指针和动态内存管理。

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

通讯录管理系统项目简介

功能说明

  1. 控制台黑窗口实现
  2. 程序需要满足以下几个功能
    在这里插入图片描述
  3. 程序开始运行时首先显示选择菜单界面,根据用户输入确定实现何种功能

程序界面

在这里插入图片描述

代码实现

多文件实现

和之前写的实战项目类似,这里同样采用多文件实现的方式
多文件写代码的方式可以让我们的写的代码的逻辑结构更加清晰,一个项目多个文件实现的形式同时也符合实际工作中一个项目的实现过程,有利于我们养成良好的编程习惯。
在这里插入图片描述
Address_Book.h:内包含项目用到的所有头文件和函数声明,以及一些宏定义和结构体声明等
Address_Book.c:这个.c文件是用来实现项目中大部分基本函数的(不包含main函数的实现)
test.c:项目主函数文件,项目主要逻辑实现(包含main函数)

项目逻辑

在这里插入图片描述

头文件部分

包含项目所要引用到的所有头文件,和一些宏定义

//↓↓↓↓↓引入要用的头文件↓↓↓↓↓
#include <stdio.h>
#include<stdlib.h>//清屏函数的头文件
#include <string.h>


//↓↓↓↓↓使用到的宏定义↓↓↓↓↓
#define MAX_NUM 100//通讯录最多存储100个联系人
#define FORMAT "%-10s %-10s %-10d %-25s %-30s\n"
#define DATA ptxl->peoples[i].name, ptxl->peoples[i].sex, ptxl->peoples[i].age, ptxl->peoples[i].phoneNumber, ptxl->peoples[i].address
//这里定义FORMAT和DATA是为了后面打印显示通讯录方便简洁,避免出现同一段代码重复出现多次的情况
//避免代码冗余

通讯录管理系统的实现是基于结构体和结构体数组的。描述一个联系人需要使用到多种类型的数据,这就要定义一个描述单个联系人的结构体,代码如下:

//创建联系人结构体
struct People
{
	char name[20];
	char sex[4];
	int age;
	char phoneNumber[12];//电话号码一般是11位数,后面加一位'\0'
	char address[30];
};

但是,描述一个通讯录的多个联系人,需要一个结构体数组,同时为了更好地统计通讯录中记录的联系人个数,也需要一个整型变量count,添加一个联系人,count加一,删除一个联系人,count减一;为了实现count和通讯录(结构体数组)之间的绑定关系,这里有定义了一个通讯录的结构体,结构体成员一个是存储联系人的信息的结构体数组,一个是统计联系人个数的整型元素count。

//创建通讯录,也就是联系人数组,最大容量为MAX_NUM,宏定义为100
struct Txl
{
	struct People peoples[MAX_NUM];
	int count ;
};

同时头文件也包含项目中函数的声明

//↓↓↓↓↓函数声明↓↓↓↓↓
void menu();//菜单函数
void initiate(struct Txl *ptxl); //初始化通讯录总数为0
void Add(struct Txl* ptxl);//添加联系人的函数
void Show(struct Txl* ptxl);//显示联系人的函数
void Find(struct Txl* ptxl);//查找联系人的函数
void Change(struct Txl* ptxl);//修改联系人的函数
void Delete(struct Txl* ptxl);//删除联系人的函数
void Clear(struct Txl* ptxl);//清空通讯录

这里说明一点,这些函数的参数都是结构体指针类型的,而不是结构体。
是因为结构体传参的时候,建议传结构体的地址。
函数在传参的时候,参数是需要压栈的,会有时间和空间上的系统开销;如果传递的是一个结构体对象的时候,结构体对象过大,参数压栈的系统开销就会比较大,会程序导致性能的下降。

内部函数实现

menu()函数的实现

menu函数的实现比较简单,主要是printf函数,代码如下:

//打印选择菜单
void menu()
{
	printf("********************************\n");
	printf("********  1.添加联系人  ********\n");
	printf("********  2.显示联系人  ********\n");
	printf("********  3.删除联系人  ********\n");
	printf("********  4.查找联系人  ********\n");
	printf("********  5.修改联系人  ********\n");
	printf("********  6.清空联系人  ********\n");
	printf("********  0.退出通讯录  ********\n");
	printf("********************************\n");
}

Add(struct Txl *ptxl)函数的实现

Add函数主要就是结构体数组的访问操作了,但是在这之前要先判断一下通讯录有没有满,也就是判断通讯录中的count成员的数值是不是等于MAX_NUM(定义的通讯录的最大容量),如果是,输出提示语,如果不是,则再进行结构体数组中单个结构体成员的访问,代码如下:

//添加联系人
void Add(struct Txl* ptxl)
{
	if (ptxl->count == MAX_NUM)
	{
		printf("通讯录已满!不能再添加联系人了~\n");
	}
	else
	{
		//添加姓名
		printf("姓名:");
		scanf("%s", ptxl->peoples[ptxl->count].name);

		//添加性别
		printf("性别(男 或 女):");
		scanf("%s", ptxl->peoples[ptxl->count].sex);

		//添加年龄
		printf("年龄:");
		scanf("%d", &ptxl->peoples[ptxl->count].age);//这里要取地址操作符!!!

		//添加联系电话
		printf("联系电话:");
		scanf("%s", ptxl->peoples[ptxl->count].phoneNumber);

		//添加地址
		printf("地址:");
		scanf("%s", ptxl->peoples[ptxl->count].address);

		(ptxl->count)++;
		printf("添加联系人成功!\n");
	}
}

Show(struct Txl *ptxl)函数的实现

Show函数的实现也比较简单,循环访问并打印结构体数组中的成员就好,循环的条件是小于通讯录结构体中的count变量的值,代码如下:

//显示联系人
void Show(struct Txl* ptxl)
{
	int i = 0;
	printf("%-10s %-10s %-10s %-25s %-30s\n", "姓名", "性别", "年龄", "联系电话", "地址");
	for (i = 0; i < (ptxl->count); i++)
	{
		printf(FORMAT,DATA);
	}
}

Delete(struct Txl *ptxl)函数的实现

根据用户输入的姓名信息删除结构体数组中的指定联系人

  1. 首先,定义一个字符类型的数组,接收用户的输入的姓名信息
  2. 然后,遍历结构体数组的每一个元素的name成员
  3. 用strcmp字符串比较函数,对用户输入和结构体数组的每一个元素的name成员进行比较,返回值用ret接收
  4. 返回值为0,则进行删除操作(就是把结构体数组成员从当前位置开始,把后一个元素赋值给前一个元素,直到循环遍历完整个结构体数组)简单来说就是用后面的元素覆盖前面的元素
  5. 接着把描述通讯录联系人总数的count进行减一操作

但是这里结构体数组中的最后一个元素并没有被覆盖但是也没有被删除,但是因为count的值进行了减一操作,所以后面打印结构体数组的时候,虽然最后一个元素没有被覆盖没有被删除,但是也不会打印出来。

代码如下:

//删除联系人
void Delete(struct Txl* ptxl)
{
	char input[20] = {0};
	int i = 0;
	int flag = 0;
	printf("请输入你要删除的联系人姓名:");
	scanf("%s",input);
	for (i = 0; i < ptxl->count; i++)
	{
		int ret = strcmp(input,ptxl->peoples[i].name);
		if (ret == 0)
		{
			flag = 1;
			int j = 0;
			int k = i;
			for (j = 0; j <(ptxl->count) - i-1; j++)
			{
				ptxl->peoples[k] = ptxl->peoples[k+1];
				k++; 
			}
			printf("删除联系人成功~\n");
			ptxl->count--;
			break;
		}
	}
	if (flag != 1)
	{
		printf("没有找到此联系人!\n");
	}
}

之中还使用了flag来标记字符串是否匹配成功,如果成功就进行删除操作,并跳出循环,否则输出提示。

Find(struct Txl *ptxl)函数的实现

Find函数的实现和Delete函数类似,也是遍历结构体数组,用strcmp函数进行匹配,匹配到了就进行打印输出,没匹配到就输出提示

//查找联系人
void Find(struct Txl* ptxl)
{
	char input[20] = { 0 };
	printf("请输入你要查找的联系人的姓名:");
	scanf("%s", &input);
	int i = 0;
	int flag = 0;//定义一个标志,找到了置为1;
	for (i = 0; i < ptxl->count; i++)
	{
		int ret = strcmp(ptxl->peoples[i].name, input);
		if (ret == 0)
		{
			printf("查找成功,该联系人相关信息如下↓↓↓\n");
			printf("%-10s %-10s %-10s %-25s %-30s\n", "姓名", "性别", "年龄", "联系电话", "地址");
			printf(FORMAT, DATA);
			flag = 1;
			break;
		}
	}
	if (flag == 0)
	{
		printf("查找失败!通讯录中没有此联系人信息!\n");
	}
}

后面函数的实现都大同小异,框架结构都类似,就不再赘述


Change(struct Txl *ptxl)函数的实现

//修改联系人
void Change(struct Txl* ptxl)
{
	char input[20] = { 0 };
	printf("请输入你要修改的联系人的姓名:");
	scanf("%s", &input);
	int i = 0;
	int flag = 0;
	for (i = 0; i < ptxl->count; i++)
	{
		int ret = strcmp(ptxl->peoples[i].name, input);
		if (ret == 0)
		{
			//姓名
			printf("姓名:");
			scanf("%s", ptxl->peoples[i].name);

			//添加性别
			printf("性别(男 或 女):");
			scanf("%s", ptxl->peoples[i].sex);

			//添加年龄
			printf("年龄:");
			scanf("%d", &ptxl->peoples[i].age);//这里要取地址操作符!!!

			//添加联系电话
			printf("联系电话:");
			scanf("%s", ptxl->peoples[i].phoneNumber);

			//添加地址
			printf("地址:");
			scanf("%s", ptxl->peoples[i].address);

			printf("联系人信息修改成功!\n");
			flag = 1;
			break;
		}
	}
	if (flag == 0)
	{
		printf("此联系人不在通讯录中!无法修改!\n");
	}
}

Clear(struct Txl *ptxl)函数的实现

void Clear(struct Txl* ptxl)
{
	ptxl->count = 0;
	printf("通讯录清空成功~~~\n");
	//这里只是简单的把结构体txl中的count值设置为0,
	//这样打印的时候就什么都不会打印,看起来像是清空了通讯录
	//实际上内存中还是存在数据的,程序结束前并没有把数组中的数据清除
	//这里具体后面在想办法改善//动态内存管理相关内容
}

主程序代码

#include "Address_Book.h"//包含自己写的头文件

int main()
{
	int input = 0;
	struct Txl txl;
	initiate(&txl);
	do 
	{
		menu();
		printf("请选择你要进行的操作->");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			//Add
			Add(&txl);
			break;
		case 2:
			//Show
			Show(&txl);
			break;
		case 3:
			Delete(&txl);
			break;
		case 4:
			Find(&txl);
			break;
		case 5:
			Change(&txl);
			break;
		case 6:
			Clear(&txl);//这里只是简单的把结构体txl中的count值设置为0,这样打印的时候就什么都不会打印,看起来像是清空了通讯录,实际上数据还是存在数组中的!!
			break;
		case 0:
			//Exit
			printf("退出系统~~~\n");
			break;
		default:
			printf("选择错误,请输入0~6 的数字!\n");
			break;
		}
	} while (input);
	return 0;
}

思考和总结

这一部分的代码还是很荣誉

  1. 以上这一些函数可以用转移表(函数指针进行优化),他们的参数和返回值类型都一致
  2. 后面写代码也发现了,遍历结构体数组,然后用strcmp库函数进行字符串匹配的这些代码多次出现,很冗余,可以封装成一个函数
  3. 删除联系人和清空联系人的操作并不是真正意义上的清除了数据和所占用的空间,后续可以使用动态内存相关知识进行优化
  4. 可以给通讯录增加一个排序功能,按名字,按年龄等
  5. 修改联系人方面可以优化,具体修改什么属性的功能
  6. 链表实现?
C语言课程设计任务书(4) 一、题目:通讯录管理 二、目的与要求 1. 目的: (1)基本掌握面向过程程序设计的基本思路和方法; (2)达到熟练掌握C语言的基本知识和技能; (3)能够利用所学的基本知识和技能,解决简单的程序设计问题 2. 要求 基本要求: 1.         要求利用C语言面向过程的编程思想来完成系统的设计; 2.       突出C语言的函数特征,以多个函数实现每一个子功能; 3.         画出功能模块图; 4.         具有清晰的程序流程图和数据结构的详细定义; 5.       熟练掌握C语言对文件的各种操作。 创新要求: 在基本要求达到后,可进行创新设计,如系统用户功能控制,对管理员级和一般级别的用户系统功能操作不同 三、信息描述 有关该系统基本信息的描述,如:姓名、电话、城市和邮编等。 四、功能描述 1.       名单基本信息(姓名,城市,电话,邮编等)的录入,并存放在文件当中。 2.       基本信息的查询与修改。 3.       记录的添加和删除。 4.       对同一类型记录的查找:如查找同一城市的记录或同一省份的记录。 五、解决方案 1.       分析程序的功能要求,划分程序功能模块。 2.       画出系统流程图。 3.       代码的编写。定义数据结构和各个功能子函数。 4.       程序的功能调试。 5.       完成系统总结报告以及使用说明书 六、进度安排 此次课程设计时间为一周或两周,分四个阶段完成: 1.       分析设计阶段。指导教师应积极引导学生自主学习和钻研问题,明确设计要求,找出实现方法,按照需求分析、总体设计、详细设计这几个步骤进行。 2.       编码调试阶段:根据设计分析方案编写C代码,然后调试该代码,实现课题要求的功能。 3.       总结报告阶段:总结设计工作,写出课程设计说明书,要求学生写出需求分析、总体设计、详细设计、编码、测试的步骤和内容。 4.       考核阶段。 七、撰写课程设计报告或课程设计总结 课程设计报告要求: 总结报告包括需求分析、总体设计、详细设计、编码(详细写出编程步骤)、测试的步骤和内容、课程设计总结、参考资料等,不符合以上要求者,则本次设计以不及格记。 八、参考资料  《C语言程序设计教程》   网上相关资料(....略)
设计一个《学生通讯录管理系统》,在动态链表程序的基础上,设计要求如下 (必须使用结构体和链表等数据结构) 1建立文件 存储文件使用指定文件名或默认文件名; 可以不保存输入记录,但需要确认是否保存输入记录 如果已有文件,只能在其后追加; 新增记录可以不存入原文件中,以可以用原来的文件覆盖内存的内容; 可以将多个个文件记录合并到一个文件中; 2文件的存取和显示 可以单独存取文件; 可以随时显示内存中记录的全部内容; 可以直接存取默认文件或指定文件; 3删除记录 可以按“姓名”或“电话”方式删除记录并更新内存链表内容; 能给出被删除的信息,输出没有找到的信息; 如果已经是空表,上出时应给出信息并返回主菜单; 如果没有要删除的信息,输出没有找到的信息; 删除操作仅限于内存,只有执行记录时,才能覆盖原记录; 4查询记录 可以按“姓名”或“电话”或“宿舍”方式查询记录 能给出查询记录的信息; 如果查询的信息不存在,输出没有找到的信息; 5 整体功能 a可以随时检索、删除、或增加新记录,保存或取消新的记录 b使姓名可由16位字符和数字的混合编码组成 c使电话号码可由18位字符和数字组成 d将输出信息加上输出信息信息栏,例如 姓名 电话 性别 年龄 生日 宿舍 李四 1234 男 21 7月1日 东二333 e使用菜单实现功能的正确的选择 f 所有节点信息都是动态生成。 6测试程序 应列出测试大纲对程序进行测试; 应保证测试用例测试到程序的各种边缘情况
评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值