【数据结构】 顺序表的应用 - 通讯录的实现

0. 前言

上一期博客中,我们已经学习了顺序表是什么,以及顺序表相关接口的实现,了解了如何实现顺序表的插入和删除等功能,那么在这期博客,我们可以基于顺序表来实现一个通讯录,在通讯录当中能实现联系人的增、删、查改等功能,接下来就让我们一起来实现通讯录吧!

1. 实现通讯录前的分析

注意:同学们需要掌握C语言中结构体、动态内存管理、顺序表、⽂件操作等相关知识。

在实现通讯录的代码前我们要先思考在通讯录项目中能实现什么样的功能

🌟🌟大致我们可以思考以下几个方面

(1)至少能够存储100个人的通讯信息
(2)能够保存用户信息:名字、性别、年龄、电话、地址等
(3)增加联系人信息
(4)删除指定联系人
(5)查找制定联系人
(6)修改指定联系人
(7)显示联系人信息

同时由于在之前的顺序表中使用的是动态顺序表

所以在实现通讯录项目中也是在动态顺序表基础上改造成我们的通讯录。 

2. 通讯录流程设计

我们可以像以前一样,将通讯录拆分为不同的文件进行设计。

我们将代码拆分为三块,如下图所示,分别是:

contact.h -- 用于写相关函数声明。

contact.c -- 用来写模块相关函数实现。

test.c -- 用于通讯录整体功能测试。

当然,以为是基于顺序表改造的通讯录,

我们还会用到之前写的顺序表的功能实现的两个文件

3. 通讯录的实现 

3.1 定义联系人的结构

联系人信息至少应该包含:名字、性别、年龄、电话、地址等

 由于顺序表的底层就是数组,所以我们就是要利用数组来实现如以下所示的结构

在通讯录中由于我们要存储的是多个联系人的信息,因此要定义一个结构体来存储联系人的信息
以下定义结构体struct PersonInfo来存储联系人的信息,并且使用typedef将该结构体重命名为Info
并且在PersonInfo中姓名,性别,电话,住址每个数组的大小可以用#define来定义。

#pragma once
#define  _CRT_SECURE_NO_WARNINGS 1

//通讯录数据类型
#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100

//通讯录数据类型
typedef struct PersonInfo
{
	char name[NAME_MAX];
	int age;
	char gender[GENDER_MAX];
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Info;

3.2 定义通讯录顺序表 

基于顺序表实现通讯录,顺序表的元素类型SLDatatype就不再是int,而是包含每个联系人信息的结构体Info。 

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "Contact.h"

typedef Info SLDataType;

typedef struct SeqList
{
	SLDataType* arr;//存储数据的底层结构 
	int capacity;//记录顺序表的空间大小 
	int size;//记录顺序表当前有效的数据个数 
}SL;//将struct Seqlist重命名为SL

3.3 通讯录菜单与主函数设计

我们的通讯录肯定是需要有一个菜单的,这样用户才能选择对通讯录执行什么样的操作,这个菜单必须包含所有通讯录功能,也包含退出。

//通讯录菜单
void menu()
{
	printf("*****************通讯录***************\n");
	printf("*******1.添加联系人  2.删除联系人*****\n");
	printf("*******3.修改联系人  4.查找联系人*****\n");
	printf("*******5.查看通讯录  0.  退 出  ******\n");
	printf("**************************************\n");
}

主函数中,实现了一个简单的通讯录操作菜单选择功能。

首先,声明了一个整型变量 op并初始化为 -1 ,用于存储用户的操作选择。

还声明了一个 Contact 类型的变量 con 创建一个通讯录,并调用 ContactInit(&con) 函数对其进行初始化。 然后,通过一个 do-while 循环来不断展示菜单,并获取用户的操作选择。 在循环内部,先调用 menu() 函数显示菜单,提示用户输入操作选项。通过 scanf("%d", &op) 读取用户输入的整数。 接下来,使用 switch 语句根据用户输入的 op值执行不同的操作。

 当 op 为 1 时,调用 ContactAdd(&con)函数执行添加联系人的操作。

当 op为 2时,调用 ContactDel(&con)函数执行删除联系人的操作。  

当 op 为 3 时,调用 ContactModify(&con)函数执行修改联系人的操作。 

当 op 为 4 时,调用 ContactFind(&con) 函数执行查找联系人的操作。

当 op 为 5 时,调用 ContactShow(&con) 函数执行显示联系人的操作。 

当 op 为 0 时,输出提示信息表示退出通讯录。

最后,在循环结束后,调用 ContactDesTroy(&con) 来销毁通讯录。

如果 op 是其他值,则输出输入错误的提示信息,

让用户重新选择。 最后,只要 op 不等于 0 ,就会继续循环执行上述操作。

int main()
{
	int op = -1;
	Contact con;
	ContactInit(&con);

	do {
		menu();
		printf("请选择您的操作:\n");
		scanf("%d", &op);

		//要根据对应的op执行不同的操作
		switch (op)
		{
		case 1:
			ContactAdd(&con);
			break;
		case 2:
			ContactDel(&con);
			break;
		case 3:
			ContactModify(&con);
			break;
		case 4:
			ContactFind(&con);
			break;
		case 5:
			ContactShow(&con);
			break;
		case 0:
			printf("退出通讯录....\n");
			break;
		default:
			printf("输入错误,请重新选择您的操作!\n");
			break;
		}
	} while (op != 0);

	ContactDesTroy(&con);
	return 0;
}

3.4 初始化 销毁 

在以上完成联系人结构体的定义以及对顺序表内数组类型的更改,

接下来就可以来实现通讯录初始化和销毁的函数了

在此先在contact.h内声明初始化函数以及销毁函数,由于在此要对通讯录内容进行更改,所以要进行传址调用,两个函数的参数都是结构体指针

下面是函数声明

void ContactInit(contact* pcon);//初始化通讯录
void ContactDestory(contact* pcon);//销毁通讯录

声明完之后就是在contact.c内实现以上两个函数,

在此通讯录的初始化以及销毁内就可以直接调用之前顺序表的初始化以及销毁函数了 

只不过我们传的是通讯录类型地址pcon,非常的方便。不需要重新实现了。

//初始化和销毁
void SLInit(SL* ps) 
{
	ps->arr = NULL; //不是int 而是PersonInfo类型 
	ps->size = ps->capacity = 0;
}

void SLDestroy(SL* ps) 
{
	assert(ps);

	if (ps->arr) 
    {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

//通讯录的初始化和销毁
void ContactInit(Contact* pcon)
{
	SLInit(pcon);
}

void ContactDesTroy(Contact* pcon) 
{
	SLDestroy(pcon);
}

3.5 通讯录的显示 

通讯录的展示就是将通讯录的信息都打印出来,实质就是要遍历一般数组
为了使我们通讯录每个人的信息格式好看,我们printf打印每个人信息时,占位符中的%后加入的整数宽度限制来让打印出的通讯录更有顺序,这里我只是空格隔开,没有追求格式,同学们实现时可以自行调整。

void ContactShow(Contact* pcon) 
{
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");

	for (int i = 0; i < pcon->size; i++)
	{
		printf("%s %s %d %s %s\n",
			pcon->arr[i].name,
			pcon->arr[i].gender,
			pcon->arr[i].age,
			pcon->arr[i].tel,
			pcon->arr[i].addr
		);
	}
}

3.6 通讯录各功能实现

在以上完成了通讯录的初始化和销毁接下来我们就来实现通讯录增、删、查、改的功能了

3.6.1 通讯录内增加联系人 

在此要实现通讯录中联系人的增加先在contact.h内对增加联系人函数进行声明,由于在此要对通讯录内容进行更改,所以要进行传址调用,函数的参数是结构体指针

void ContactAdd(Contact* pcon);通讯录内增加联系人

接下来就是在contact.c内完成添加联系人的函数

在此先创建一个结构体 Info info;,并且使用scanf来读取用户输入的此联系人的各信息存储在结构体info内,再调用顺序表中的插入函数将该结构体info插入到数组内,在此使用的是尾插函数,也可以使用其他插入方法

//增加联系人到通讯录
void ContactAdd(Contact* pcon) 
{
	//创建联系人结构体变量
	Info info;

	printf("请输入联系人姓名:\n");
	scanf("%s", info.name);
	printf("请输入联系人年龄:\n");
	scanf("%d", &info.age);
	printf("请输入联系人性别:\n");
	scanf("%s", info.gender);
	printf("请输入联系人电话:\n");
	scanf("%s", info.tel);
	printf("请输入联系人住址:\n");
	scanf("%s", info.addr);

	//保存数据到通讯录(顺序表)
	SLPushBack(pcon, info);
}

插入元素就是对顺序表尾插的操作

//顺序表的尾部插入
void SLPushBack(SL* ps, SLDataType x) 
{
	//断言--粗暴的解决方式
	//assert(ps != NULL);
	assert(ps);

	//if判断--温柔的解决方式
	//if (ps == NULL) {
	//	return;
	//}

	//空间不够,扩容
	SLCheckCapacity(ps);

	//空间足够,直接插入
	ps->arr[ps->size++] = x;
	//ps->size++;
}

扩容:

void SLCheckCapacity(SL* ps) 
{
	if (ps->size == ps->capacity) 
    {
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
		if (tmp == NULL) 
        {
			perror("realloc fail!");
			exit(1);
		}
		//扩容成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

3.6.2 通讯录内删除联系人

在此要实现通讯录中联系人的删除先在contact.h内对删除联系人函数进行声明,由于在此要对通讯录内容进行更改,所以要进行传址调用,函数的参数是结构体指针

void ContactDel(Contact* pcon);//通讯录内删除联系人

在声明完函数之后就是对该函数的实现,但在删除通讯录内的联系人要通讯录中存在要删除的联系人才能删除,所以在函数内还要先判断,但之后通讯录的其他功能可能还要用到查找联系人是否存在

所以可以直接在创建一个判断相关联系人是否存在的函数,在此根据的是名字来查找联系人

存在该联系人就返回相应的数组下标,不存在就返回-1

int FindByName(Contact* pcon, char name[]) 
{
	for (int i = 0; i < pcon->size; i++)
	{
		if (strcmp(pcon->arr[i].name, name) == 0) 
        {
			//找到了
			return i;
		}
	}
	return -1;
}

接下来就是在contact.c内完成删除联系人的函数

在此函数内先定义一个char类型的数组name,大小为NAME_MAX。用scanf将用户输入的联系人姓名存放在该数组内,之后再将指针con与name作为FindbyName的参数,通过FindbyName函数的返回值来得到要删除的联系人是否存在。若返回值小于0则说明该联系人不存在,之后就直接退出函数ContactDel,否则就调用顺序表中的任意位置的删除函数SLErase,这时findIndex就是要删除的数组下标

void ContactDel(Contact* pcon) 
{
	//删除之前一定要先查找
	//找到了,可以删除
	//找不到,不能执行删除
	printf("请输入要删除的联系人姓名:\n");
	char name[NAME_MAX];
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);
	if (findIndex < 0) 
    {
		printf("要删除的联系人不存在!\n");
		return;
	}
	//执行删除操作
	SLErase(pcon, findIndex);
	printf("联系人删除成功!\n");
}

删除就是顺序表中指定元素删除操作

//删除指定位置数据
void SLErase(SL* ps, int pos) 
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	//pos以后的数据往前挪动一位
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//ps->arr[i-2] = ps->arr[i-1];
	}
	ps->size--;
}

3.6.3 通讯录内修改联系人

在此要实现通讯录中联系人的修改先在contact.h内对修改联系人函数进行声明,由于在此要对通讯录内容进行更改,所以要进行传址调用,函数的参数是结构体指针

void ContactModify(Contact* pcon);//通讯录内修改联系人

接下来就是在contact.c内完成修改联系人的函数

在该函数内和删除联系人函数一样也是先在此函数内先定义一个char类型的数组name,大小为NAME_MAX。用scanf将用户输入的联系人姓名存放在该数组内,之后再将指针con与name作为FindbyName的参数,通过FindbyName函数的返回值来得到要修改的联系人是否存在。若返回值小于0则说明该联系人不存在,之后就直接退出函数ContactDel。否则就使用scanf将用户输入的信息存放到原来联系人的数组位置

void ContactModify(Contact* pcon) 
{
	//修改之前要先查找
	//找到了,执行修改操作
	//没有找到,不能执行修改操作

	char name[NAME_MAX];
	printf("请输入要修改的联系人姓名:\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);
	if (findIndex < 0) 
	{
		printf("要修改的联系人不存在!\n");
		return;
	}
	printf("请输入姓名:\n");
	scanf("%s", pcon->arr[findIndex].name);
	printf("请输入年龄:\n");
	scanf("%d", &pcon->arr[findIndex].age);
	printf("请输入性别:\n");
	scanf("%s", pcon->arr[findIndex].gender);
	printf("请输入电话:\n");
	scanf("%s", pcon->arr[findIndex].tel);
	printf("请输入地址:\n");
	scanf("%s", pcon->arr[findIndex].addr);

	printf("联系人修改成功!\n");
}

3.6.4 通讯录内查找联系人

 在此要实现通讯录中联系人的修改先在contact.h内对修改联系人函数进行声明,在此虽然查找联系人未对通讯录内数据进行修改,但在此还是将结构体作为函数的参数,原因通讯录其他函数都是传地址在此也保持一致性

void ContactFind(Contact* pcon);//在通讯录内查找联系人

接下来就是在contact.c内完成查找联系人的函数

和前面一样也是先在此函数内先定义一个char类型的数组name,大小为NAME_MAX,然后,提示用户输入要查找的姓名,并通过 scanf 函数获取输入。

接下来,调用 FindByName 函数在通讯录中查找该姓名对应的索引 findIndex 。

如果 findIndex 小于 0,说明未找到该联系人,输出提示信息并直接返回。

如果找到了,即 findIndex 非负,打印出联系人的详细信息,包括姓名、性别、年龄、电话和住址。

void ContactFind(Contact* pcon) 
{
	char name[NAME_MAX];
	printf("请输入要查找的用户姓名:\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);
	if (findIndex < 0) 
	{
		printf("该联系人不存在!\n");
		return;
	}
	//找到了,打印一下查找的联系人信息
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	printf("%s %s %d %s %s\n",
		pcon->arr[findIndex].name,
		pcon->arr[findIndex].gender,
		pcon->arr[findIndex].age,
		pcon->arr[findIndex].tel,
		pcon->arr[findIndex].addr
	);
}

 4. 通讯录读取历史数据和保存数据

在之前已经实现了通讯录的各功能的数据只能在程序运行窗口打开的时候进行通讯录的增、删、查、改等功能。在关闭窗口后对通讯录进行的各项操作都不会保存,那么要怎么样才能让我们设计的通讯录在每次开始之前都读取之前的信息,在结束后都保存通讯录的信息呢?

在之前文件操作章节中讲解了如何将程序数据输出到文件中,将文件数据输入到程序当中,所以在通讯录中我们就可以用到文件操作的相关函数来实现通讯录数据的保存与读取。

4.1读取历史数据

要读取历史数据就要在通讯录每次初始化之后就输入文件的信息到所创建的通讯录中,也就是将输入到数组当中。

在此先在我们创建的程序的文件夹中创建一个contact.txt文件,再使用fopen以读的方式打开文件,再创建一个Info info的变量,之后在使用到fread以二进制的形式输入文件的信息到创建的info中,再将info尾插到数组当中。

void ContactRead(Contact* pcon)//从文件中读取历史数据
{
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	Info info;
	while (fread(&info, sizeof(Info), 1, pf))
	{
		SLPushBack(pcon, info);
	}
	printf("成功读取历史数据到通讯录中\n");
}

 在通讯录初始化函数中调用SLInit函数后调用ContactRead就可以实现历史数据的读取

void ContactInit(Contact* pcon)//通讯录初始化
{
	SLInit(pcon);
	ContactRead(pcon);
}

4.2 保存数据

要实现通讯录每次在退出程序后都能将数据保留,这就需要在每次销毁通讯录前将通讯录内的数据,也就是数组的所有元素都输出到contact.txt文件内。

要把数组的所有元素都输出到contact.txt文件内就需要先以写的方式打开文件,后在循环的使用fwrite将数组的数据以二进制的形式输出到文件当中

void SaveContact(Contact* con) 
{
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL) 
	{
		perror("fopen error!\n");
		return;
	}
	//将通讯录数据写⼊⽂件
	for (int i = 0; i < con->size; i++)
	{
		fwrite(con->a + i, sizeof(Info), 1, pf);
	}
	printf("通讯录数据保存成功!\n");
}

在通讯录销毁函数中调用SLDestroy函数前调用SaveContact就可以实现数据输出到文件当中,也就将数据保留了下来

void ContactDestroy(Contact* pcon)//销毁通讯录
{
	SaveContact(pcon);
	SLDestroy(pcon);
}

5. 通讯录完整代码 

注:在Seqlist.h和Seqlist.c内的查找和打印顺序表与以上代码不兼容,运行时会使得程序崩溃,所以将这些部分注释掉 

Contact.h

#pragma once
#define  _CRT_SECURE_NO_WARNINGS 1

//通讯录数据类型
#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100

//通讯录数据类型
typedef struct PersonInfo
{
	char name[NAME_MAX];
	int age;
	char gender[GENDER_MAX];
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Info;

//使用顺序表的前置声明
struct SeqList;

typedef struct SeqList Contact;
//通讯录提供的操作 
//通讯录的初始化和销毁

void ContactInit(Contact* pcon);//实际初始化的还是顺序表
void ContactDesTroy(Contact* pcon);

//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon);
void ContactDel(Contact* pcon);
void ContactModify(Contact* pcon);
void ContactFind(Contact* pcon);
void ContactShow(Contact* pcon);

 Seqlist.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "Contact.h"

//动态顺序表
typedef Info SLDataType;

typedef struct SeqList
{
	SLDataType* arr;//存储数据的底层结构 
	int capacity;//记录顺序表的空间大小 
	int size;//记录顺序表当前有效的数据个数 
}SL;

//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);//保证接口的一致性


//顺序表头部 尾部插⼊
void SLPushBack(SL* ps, SLDataType x);
void SLPushFront(SL* ps, SLDataType x);

//顺序表的头部 尾部删除
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);

//指定位置之前插入数据
//删除指定位置数据 
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);

//在顺序表中查找x
int SLFind(SL* ps, SLDataType x);

Seqlist.c 

#include"SeqList.h"
//初始化和销毁

void SLInit(SL* ps) 
{
	ps->arr = NULL; //不是int 而是Info类型 
	ps->size = ps->capacity = 0;
}

void SLCheckCapacity(SL* ps) 
{
	if (ps->size == ps->capacity) 
	{
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
		if (tmp == NULL) 
		{
			perror("realloc fail!");
			exit(1);
		}
		//扩容成功
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

//顺序表的头部/尾部插入
void SLPushBack(SL* ps, SLDataType x) 
{
	//断言--粗暴的解决方式
	//assert(ps != NULL);
	assert(ps);

	//if判断--温柔的解决方式
	//if (ps == NULL) 
	// {
	// 
	//	return;
	//}

	//空间不够,扩容
	SLCheckCapacity(ps);

	//空间足够,直接插入
	ps->arr[ps->size++] = x;
	//ps->size++;
}
void SLPushFront(SL* ps, SLDataType x) 
{
	assert(ps);

	//判断是否扩容
	SLCheckCapacity(ps);

	//旧数据往后挪动一位
	for (int i = ps->size; i > 0; i--) //i = 1
	{
		ps->arr[i] = ps->arr[i - 1]; //ps->arr[1] = ps->arr[0]
	}
	ps->arr[0] = x;
	ps->size++;
}

//顺序表的头部/尾部删除
void SLPopBack(SL* ps) 
{
	assert(ps);
	assert(ps->size);

	//顺序表不为空
	//ps->arr[ps->size - 1] = -1;
	ps->size--;
}
void SLPopFront(SL* ps) 
{
	assert(ps);
	assert(ps->size);

	//不为空执行挪动操作
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType x) 
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);

	SLCheckCapacity(ps);

	//pos及之后的数据往后挪动一位,pos空出来
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1]; //ps->arr[pos+1] = ps->arr[pos]
	}
	ps->arr[pos] = x;
	ps->size++;
}
//删除指定位置数据
void SLErase(SL* ps, int pos) 
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	//pos以后的数据往前挪动一位
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//ps->arr[i-2] = ps->arr[i-1];
	}
	ps->size--;
}

//在顺序表中查找X
//int SLFind(SL* ps, SLDataType x) 
//{
//	//加上断言对代码的健壮性更好
//	assert(ps);
//	for (int i = 0; i < ps->size; i++)
//	{
//		if (ps->arr[i] == x) 
//	{
//			return i;
//		}
//	}
//	return -1;
//}

void SLDestroy(SL* ps) 
{
	assert(ps);

	if (ps->arr) 
    {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

void SLPrint(SL* ps) 
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

Contact.c 

#include"Contact.h"
#include"SeqList.h"

//通讯录的初始化和销毁
//SL* ps
void ContactInit(Contact* pcon) {
	SLInit(pcon);
}

void ContactDesTroy(Contact* pcon) {
	SLDestroy(pcon);
}

//增加、删除、修改、查找、查看通讯录
void ContactAdd(Contact* pcon) 
{
	//创建联系人结构体变量
	Info info;

	printf("请输入联系人姓名:\n");
	scanf("%s", info.name);
	printf("请输入联系人年龄:\n");
	scanf("%d", &info.age);
	printf("请输入联系人性别:\n");
	scanf("%s", info.gender);
	printf("请输入联系人电话:\n");
	scanf("%s", info.tel);
	printf("请输入联系人住址:\n");
	scanf("%s", info.addr);

	//保存数据到通讯录(顺序表)
	SLPushBack(pcon, info);
}

int FindByName(Contact* pcon, char name[]) {
	for (int i = 0; i < pcon->size; i++)
	{
		if (strcmp(pcon->arr[i].name, name) == 0) {
			//找到了
			return i;
		}
	}
	return -1;
}


void ContactDel(Contact* pcon) {
	//删除之前一定要先查找
	//找到了,可以删除
	//找不到,不能执行删除
	printf("请输入要删除的联系人姓名:\n");
	char name[NAME_MAX];
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);
	if (findIndex < 0) {
		printf("要删除的联系人不存在!\n");
		return;
	}
	//执行删除操作
	SLErase(pcon, findIndex);
	printf("联系人删除成功!\n");
}

void ContactModify(Contact* pcon) 
{
	//修改之前要先查找
	//找到了,执行修改操作
	//没有找到,不能执行修改操作

	char name[NAME_MAX];
	printf("请输入要修改的联系人姓名:\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);
	if (findIndex < 0) 
	{
		printf("要修改的联系人不存在!\n");
		return;
	}
	printf("请输入姓名:\n");
	scanf("%s", pcon->arr[findIndex].name);
	printf("请输入年龄:\n");
	scanf("%d", &pcon->arr[findIndex].age);
	printf("请输入性别:\n");
	scanf("%s", pcon->arr[findIndex].gender);
	printf("请输入电话:\n");
	scanf("%s", pcon->arr[findIndex].tel);
	printf("请输入地址:\n");
	scanf("%s", pcon->arr[findIndex].addr);

	printf("联系人修改成功!\n");
}

void ContactShow(Contact* pcon) 
{
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");

	for (int i = 0; i < pcon->size; i++)
	{
		printf("%s %s %d %s %s\n",
			pcon->arr[i].name,
			pcon->arr[i].gender,
			pcon->arr[i].age,
			pcon->arr[i].tel,
			pcon->arr[i].addr
		);
	}
}

void ContactFind(Contact* pcon) 
{
	char name[NAME_MAX];
	printf("请输入要查找的用户姓名:\n");
	scanf("%s", name);

	int findIndex = FindByName(pcon, name);
	if (findIndex < 0) 
	{
		printf("该联系人不存在!\n");
		return;
	}
	//找到了,打印一下查找的联系人信息
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "住址");
	printf("%s %s %d %s %s\n",
		pcon->arr[findIndex].name,
		pcon->arr[findIndex].gender,
		pcon->arr[findIndex].age,
		pcon->arr[findIndex].tel,
		pcon->arr[findIndex].addr
	);
}

void SaveContact(Contact* con) 
{
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL) 
	{
		perror("fopen error!\n");
		return;
	}
	//将通讯录数据写⼊⽂件
	for (int i = 0; i < con->size; i++)
	{
		fwrite(con->a + i, sizeof(Info), 1, pf);
	}
	printf("通讯录数据保存成功!\n");
}

void ContactRead(Contact* pcon)//从文件中读取历史数据
{
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	Info info;
	while (fread(&info, sizeof(Info), 1, pf))
	{
		SLPushBack(pcon, info);
	}
	printf("成功读取历史数据到通讯录中\n");
}

TestContact.c 

//#include"Contact.h"  //在SeqList.h文件中已经包了Contact.h
#include"SeqList.h"

//通讯录菜单
void menu()
{
	printf("*****************通讯录***************\n");
	printf("*******1.添加联系人  2.删除联系人*****\n");//ctrl+d
	printf("*******3.修改联系人  4.查找联系人*****\n");//ctrl+d
	printf("*******5.查看通讯录  0.  退 出  ******\n");//ctrl+d
	printf("**************************************\n");
}
int main()
{
	int op = -1;
	//创建通讯录结构对象
	Contact con;
	ContactInit(&con);
	do {
		menu();
		printf("请选择您的操作:\n");
		scanf("%d", &op);

		switch (op)
		{
		case 1:
			//添加联系人
			ContactAdd(&con);
			break;
		case 2:
			//删除联系人
			ContactDel(&con);
			break;
		case 3:
			//修改联系人
			ContactModify(&con);
			break;
		case 4:
			//查找联系人
			ContactFind(&con);
			break;
		case 5:
			//查看通讯录
			ContactShow(&con);
			break;
		case 0:
			//退出通讯录
			printf("通讯录退出中...\n");
			break;
		default:
			break;
		}

	} while (op != 0);
	//销毁通讯录
	ContactDesTroy(&con);
	return 0;
}

大家可以根据上述思路和提供的源码,自己练习哦~

以上就是本篇的所有内容了,希望对你的学习有所帮助~

对于我博客中内容有疑问的话,欢迎大家在评论区提问,发表你的看法

我们下期再见!👋👋

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值