【c语言进阶】软工期末大作业 —— 通讯录的实现 ,静态动态双版本

文章介绍了如何创建一个通讯录程序,包括静态版本和动态版本。静态版本使用固定大小的数组存储联系人,而动态版本则利用动态内存管理,通过malloc和realloc适应不同数量的联系人。文章详细讲解了各个功能的实现,如添加、删除、查找、修改和排序联系人,并讨论了内存管理的重要性。
摘要由CSDN通过智能技术生成

请添加图片描述

1. 创建通讯录的大概思路

  1. 首先,我们要知道,通讯录的大小(100个人或者10000个人等等),其次,我们要知道通讯录中,一个人的信息都有哪儿些,好让我们创造变量
  2. 实现通讯录的功能(增删查改……)

2. 静态版本

之所以叫静态版本,是因为通讯录的大小是固定的,也就是说,我们将使用数组直接定义通讯录的大小。

2.1 通讯录菜单

在一个程序运行过程中,我们需要提示用户,进行其想执行的的操作,所以,我们需要在程序开始时,先写一个菜单。
代码如下:

void meun()
{
	printf("****************************************\n");
	printf("******    1. Add       2. Del    *******\n");
	printf("******    3. Seek      4. Modify *******\n");
	printf("******    5. Show      6. Sort   *******\n");
	printf("******    7. Empty     0. Exit   *******\n");
	printf("****************************************\n");
}

2.2 通讯录主函数

本次项目,我们依旧采用多窗口模式,主函数即我们的测试窗口。

为了使程序可以循环进行,我们选择使用do……while函数、switch……case函数相结合
代码如下:

int main()
{
	int input = 0;
	Contact con;
	InitContact(&con);
	do
	{
		meun();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case Exit:
			printf("退出通讯录\n");
			break;
		case Add:
			AddContact(&con);
			break;
		case Del:
			DelContact(&con);
			break;
		case Seek:
			SeekContact(&con);
			break;
		case Modify:
			ModifyContact(&con);
			break;
		case Show:
			ShowContact(&con);
			break;
		case Sort:
			SortContact(&con);
			ShowContact(&con);
			break;
		case Empty:
			EmptyContact(&con);
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;

		}
	} while (input);
	return 0;
}

有细心的uu发现,我们的case 后面跟的不是常数,而是一些“变量”,其实这是使用了枚举的结果
为什么使用枚举呢?

  1. 增加代码的可读性和可维护性
  2. 便于调试
  3. 枚举中的默认赋值,与我们的菜单相联系。

代码如下:

enum Option
{
	Exit,
	Add,
	Del,
	Seek,
	Modify,
	Show,
	Sort,
	Empty
};

2.3 结构体定义联系人和通讯录

//表示一个人的信息
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
{
	PeoInfo data[MAX];//存放数据
	int sz;//记录通讯录中的有效信息的个数
}Contact;

2.4 全局变量的声明

有的uu发现,在上面结构体的定义中,数组中使用的时常变量,而不是数值,是因为,这有便于我们,后期的改值。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 4
#define MAX_TELE 20
#define MAX_ADDR 30

2.5 初始化通讯录

void InitContact(Contact* pc)
{
	assert(pc != NULL);

	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

有的uu会上来就把 pc->data = 0; 这个代码写上去,这样是不是有点太草率了,我们data中存放的地址,而直接把其全部改成0,是否有些不妥呢?
所以我们使用了memset函数,pc->data中的每一个字节修改成0.

2.6 实现通讯录功能

做完上述的工作,我们终于该开始实现通讯录的各种功能了。

2.6.1 添加联系人

//非法返回-1,否则返回这个数
static int JudgeIsLegal(const Contact* pc)
{
	assert(pc != NULL);
	if (pc->sz >= MAX && pc->sz < 0)
		return -1;
	return pc->sz;
}
void AddContact(Contact* pc)
{
	assert(pc != NULL);
	if (JudgeIsLegal(pc) == -1)
		return;
	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");
}

注意事项:

  1. 首先我们需要注意的时在输入过程中,scanf的参数类型时指针,我们需要传入地址,这里的年龄就需要我们使用&取地址操作符,而其他的变量因为都是数组,不需要&取地址符,所以可能会忽略年龄变量所需要的&取地址操作符。
  2. 这里因为以后会经常判断data数组是否合法(满了、为空(删除)),所以创建了一个数组,减少冗余的代码。

2.6.2 删除联系人

//找到了返回下标i,没找到返回0
static int FindMyCon(const Contact* pc, const char* name)
{
	assert(pc != NULL);
	assert(name != NULL);
	
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
			return i;
	}
	return -1;
}
void DelContact(Contact* pc)
{
	assert(pc != NULL);
	char name[100] = { 0 };
	if (JudgeIsLegal(pc) == 0 || JudgeIsLegal(pc) == -1)
		return;
	printf("请输入要删除人的姓名:>");
	scanf("%s", name);
	int pos = FindMyCon(pc, name);
	if (-1 == pos)
	{
		printf("不存在要删除的联系人\n");
		return;
	}
	int i = 0;
	for (i = pos; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}

	pc->sz--;
	printf("删除成功\n");
}

注意事项:

  1. 在判断是否合法函数中,data数组为0对于删除函数也是非法操作,所以小编这样写。

2.6.3 查找联系人

void SeekContact(const Contact* pc)
{
	assert(pc != NULL);
	char name[100] = { 0 };
	printf("请输入要查找人的姓名:>");
	scanf("%s", name);
	int pos = FindMyCon(pc, name);
	if (-1 == pos)
	{
		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);
}

2.6.4 修改联系人

void ModifyContact(Contact* pc)
{
	assert(pc != NULL);

	char name[100] = { 0 };
	printf("请输入要修改人的姓名:>");
	scanf("%s", name);
	int pos = FindMyCon(pc, name);
	if (-1 == pos)
	{
		printf("不存在要修改的联系人\n");
		return;
	}
	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");
}

2.6.5 显示通讯录

void ShowContact(const Contact* pc)
{
	assert(pc != NULL);
	printf("%-10s %-4s %-5s %-12s %-30s\n", 
		"姓名", "年龄", "性别", "手机号", "地址");
	int i = 0;
	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);
	}
}

2.6.6 通讯录排序

int name_cmp(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
void SortContact(Contact* pc)
{
	assert(pc != NULL);
	qsort(pc, pc->sz, sizeof(PeoInfo), name_cmp);
}

注意:

  1. 在这里小编使用了qsort函数,如果对qsort函数不了解的uu可以看看小编的另一篇文章里边有实现。qsort函数的冒泡实现

3. 动态版本

  1. 之所以叫动态版本,是因为,其所占用内存的空间时可以变化的,这里,我们就使用了动态内存管理中的malloc、free、realloc等函数。
  2. 如果没有学过动态内存管理的uu也先不用着急,小编的下一篇就会更新有关动态内存管理的知识,可以先把静态通讯录自己敲一遍哦。

3.1 结构体定义联系人和通讯录

  1. 在静态中,我们定义通讯录时,使用了data数组,因为我们的目标时可变的,而数组是固定的内存空间,所以,我们不能使用数组来定义了,这里使用了malloc函数。
typedef struct PeoInfo
{
	char Name[Name_Max];
	int SZ;
	char Sex[Sex_Max];
	char Tele[Tele_Max];
	char Addr[Addr_Max];

}PeoInfo;

typedef struct Contact
{
	PeoInfo* data;
	int sz;
	int capacity;

}Contact;

细心的uu已经发现,我们在通讯录这个结构体中多了一个capacity(容量)变量,这是因为,在联系人的增加或者删减中,我们需要一直判断,内存是否足够,如果不够,我们则需要增加内存空间
这里在添加联系人函数中有具体体现。

3.2 全局变量的声明

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define Name_Max 20
#define Sex_Max 4
#define Tele_Max 13
#define Addr_Max 30

#define Inti_capacity 3
#define Add_capacity 2

3.3 初始化通讯录

void IntiContact(Contact* pc)
{
	pc->data = (PeoInfo*)malloc(Inti_capacity * sizeof(PeoInfo));
	if (pc->data = NULL)
	{
		perror("通信录初始化失败",errno);
		return;
	}
	pc->capacity = Inti_capacity;
	pc->sz = 0;
}

3.4 实现通讯录功能

3.4.1 添加联系人

这里的CheckCapacity函数很重要,请uu们认真思考。

static int CheckCapacity(Contact* pc)
{
	if (pc->capacity == pc->sz)
	{
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + Add_capacity) * sizeof(PeoInfo));
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += Add_capacity;
			return 1;
		}
	}
	return 0;
}
void AddContact(Contact* pc)
{
	if (0 == CheckCapacity(pc))
	{
		printf("空间不够,增容失败\n");
		return;
	}
	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");
	}
}

3.4.2 退出通讯录

void DestoryContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

因为使用了动态内存,所以在不使用时,我们需要将其归还操作系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值