C语言实现通讯录--动态版

一、题目要求

实现一个通讯录,联系人的数量可多可少

二、解题思路

1.在静态版本的基础上改用动态的方法:

(1)默认能够存放三个人的信息

(2)不够的话,每次增加两个人的信息

2.其他功能不变

三、模块划分

建立三个文件:

test.c 用于测试通讯录的相关功能

contsct.c 通讯录的实现模块(用函数实现功能)

contact.h 声明(函数的声明)

四、代码实现

test.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
//C语言实现通讯录--动态版
/*
解题思路
1.在静态版本的基础上改用动态的方法:
(1)默认能够存放三个人的信息
(2)不够的话,每次增加两个人的信息
2.其他功能不变
*/
//菜单
#include "contact.h"
void menu() {
	printf("****************************************************\n");
	printf("**********1.add*********2.del***********************\n");
	printf("**********3.search******4.modify********************\n");
	printf("**********5.show********6.sort**********************\n");
	printf("**********0.exit************************************\n");
}
//通讯录功能用枚举方法列举出来,提高代码的可读性
/*
在主函数中的switch...case...语句中选择功能时,case 1,2,3...这样的选项不能让代码阅读者清晰地联想到各个数字代表实现什么功能
但是使用枚举,在case语句中用case 1代表增加联系人的时候就可写成case add
当枚举中的选项和菜单上的数字匹配上之后,在case语句中想实现哪个功能,写哪个选项就可以了
这样的话,case里面的选项和我们想实现的功能的意思就关联起来了
*/
enum Option {
	Exit,
	add,
	del,
	search,
	modify,
	show,
	sort
};
int main() {
	int input = 0;
	Contact con; //用结构体类型创建一个结构体变量
	//初始化通讯录
	InitContact(&con);

	do {
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input) {
		case add:
		{
			AddContact(&con);
			break;
		}
		case del:
		{
			DelContact(&con);
			break;
		}
		case search:
		{
			SearchContact(&con);
			break;
		}
		case modify:
		{
			ModifyContact(&con);
			break;
		}
		case show:
		{
			ShowContact(&con);
			break;
		}
		case sort:
		{
			SortContact(&con);
			break;
		}
		case Exit:
		{
			DestroyContact(&con);//销毁通讯录:整个通讯录都是动态开辟来的,退出时要释放掉
			printf("退出通讯录\n");
			break;
		}
		default:
		{
			printf("选择错误\n");
			break;
		}
		}
	} while (input);

	return 0;
}

contact.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

//初始化通讯录
void InitContact(Contact* pc) {
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
	if (pc->data == NULL) {
		printf("通讯录初始化失败:%s\n", strerror(errno));
		return;
	}
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;
}
//检测容量的函数
//扩容成功,返回1
//扩容失败,返回0
void CheckCapacity(Contact* pc) {
	if (pc->sz == pc->capacity) { //有效信息个数等于容量的时候,需要扩容
		PeoInfo* ptr=(PeoInfo*)realloc(pc->data, (pc->capacity + INC_sz) * sizeof(PeoInfo));//将扩容后的地址交给新指针
		//判断扩容是否成功
		//扩容失败,打印错误信息,返回
		//扩容成功,将新指针交给pc->data维护,通讯录容量变动
		if (ptr == NULL) {
			printf("CheckCapacity:%s\n", strerror(errno));
			return 0;
		}
		else {
			pc->data = ptr;
			pc->capacity += INC_sz;
			printf("增容成功,当前容量:%d\n", pc->capacity);
			return 1;
		}
	}
	//如果通讯录未满,不进入上面的if语句,直接返回1
	return 1;
}

//增容
void AddContact(Contact* pc) {
	//对检测通讯录容量的函数的返回值进行判断
	if (0 == CheckCapacity) {
		printf("空间不够,扩容失败\n");
		return 0;
	}
	else {
		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");
	}
}

//销毁通讯录
void DestroyContact(Contact* pc) {
	free(pc->data);  
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	printf("释放内存\n");
}

//显示联系人信息
void ShowContact(Contact* pc) {
	int i = 0;
	//打印标题
	//在%后面加上负号表示左对齐的方式,在%s的s前面加上数字可以限制打印出来最多几个字节
	printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (i = 0; i < pc->sz; i++) {
		printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
	}
}
//实际上,删查改联系人都需要先在通讯录中查找
//为了减少代码的冗余度,可以封装一个查找的函数FindByName
//封装的这个函数前面加static,这个函数只能在自己所在的.c文件中使用,其他的源文件看不见该函数,对它加以保护
//根据名字在通讯录中查找下标的函数
static 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
	/*
	不要写成
	if(i==pc->sz){
		return -1;
	}
	因为写成这样,if进去,条件成立才有返回,不成立无返回
	编译器在编译时发现这样的代码考虑不周全,有些情况是存在返回值的,就会发出警告
	所以,严谨的情况下,直接返回既清晰又严谨
	*/
}


//删除联系人函数
void DelContact(Contact* pc) {
	if (pc->sz == 0) {
		printf("通讯录为空,无法删除\n");
		return;
	}
	//1.根据要删除的人的名字找到在通讯录中的下标
	char name[MAX_NAME] = { 0 };
	printf("请输入要删除的人的名字:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1) {
		printf("要删除的人不存在\n");
		return;
	}
	//2.删除pos位置上的数据
	int i = 0;
	for (i = pos; i < pc->sz; i++) {
		pc->data[i] = pc->data[i + 1];//从pos下标处开始覆盖
	}
	pc->sz--;
	printf("删除成功\n");
}
//查找联系人 
void SearchContact(const Contact* pc) {//查找不需要修改通讯录,使用const加以保护,防止被修改
	char name[MAX_NAME] = { 0 };
	printf("请输入要查找的人的名字:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1) {
		printf("要查找的人不存在\n");
		return;
	}
	//找到的话直接打印这个联系人的信息
	printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-10s %-4d %-5s %-12s %-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) {
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改的人的名字:");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (pos == -1) {
		printf("要修改的人不存在\n");
		return;
	}
	//修改:把要修改人下标为pos的信息全都再录入一遍
	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");
}
//排序联系人
//使用qsort进行排序,要自定义一个排序方法
int cmp_by_name(const void* e1, const void* e2) {//e1,e2指针各自指向一个人的信息
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc) {
	//按照名字来排序
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
	printf("排序成功\n");
	//调用显示联系人函数ShowContact,自动打印一下排序后的结果
	ShowContact(pc);
}

contact.h

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30

#define DEFAULT_SZ 3 //默认是3个
#define INC_sz 2  //扩容每次增加2个

//结构体存放每个联系人的信息
typedef struct PeoInfo { //typedef:给已定义的变量类型起个别名,这里的作用是给struct Peoinfo起个别名PeoInfo
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char addr[MAX_ADDR];
}PeoInfo;
//结构体存放通讯录
typedef struct Contact {
	PeoInfo *data; //data指向存放数据的空间
	int sz;        //sz记录通讯录中的有效信息个数
	int capacity; //通讯录当前的容量
}Contact, * pContact;//*pContact意思是将结构体指针struct Contact*重命名为pContact

//初始化通讯录--传址
void InitContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);
//增加指定联系人
void AddContact(Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
/*
传址可以写成void ...Contact(pContact pc);
这里通过pContact定义出来的指针也是结构体指针
*/
//显示联系人信息
void ShowContact(Contact* pc);
//查找联系人 
void SearchContact(const Contact* pc);
//修改联系人
void ModifyContact(Contact* pc);
//排序联系人
void SortContact(Contact* pc);

五、运行结果

相关推荐
蜜桃小阿雯几秒前
JAVA开源项目 微服务在线教育系统 计算机毕业设计
java·开发语言·spring boot·微服务·java-ee·开源·maven
花下的晚风几秒前
单元测试时报错找不到@SpringBootConfiguration
java·开发语言·单元测试
爱编程— 的小李10 分钟前
结构体(c语言)
c语言·开发语言
言之。14 分钟前
【K-Means】
算法·机器学习·kmeans
fathing22 分钟前
c# 调用c++ 的dll 出现找不到函数入口点
开发语言·c++·c#
前端青山44 分钟前
webpack指南
开发语言·前端·javascript·webpack·前端框架
hummhumm1 小时前
第 10 章 - Go语言字符串操作
java·后端·python·sql·算法·golang·database
Jeffrey_oWang1 小时前
软间隔支持向量机
算法·机器学习·支持向量机
nukix1 小时前
Mac Java 使用 tesseract 进行 ORC 识别
java·开发语言·macos·orc
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】内存可见性问题 & volatile
java·开发语言·java-ee