关注

C语言实战:简单易懂通讯录(基于顺序表)

前言:

        以下是一个基于 C 语言的实战,基于数据结构中的顺序表实现通讯录程序设计,包含完整的功能实现和代码注释,适合初学者学习和实践,这个程序实现了联系人的添加、显示、查找、修改、删除以及数据持久化等核心功能,以下是实现通讯录所需的五个文件。

文件一Contact.h通讯录的头文件,定义了联系人信息结构体,同时声明了通讯录的业务接口。
文件二Contact.c通讯录功能的实现文件,基于顺序表操作封装了通讯录的业务逻辑,实现了Contact.h中声明的函数
文件三SeqList.h顺序表的头文件,定义了顺序表的核心数据结构SL,为底层存储提供接口规范。
文件四SeqList.c顺序表功能的实现文件,实现了SeqList.h中声明的所有函数
文件五Test.c程序入口文件,仅包含main函数,通过调用menu()函数启动通讯录的交互界面,是程序运行的起点。

        

一、通讯录项目的准备知识

        

①本项目基于数据结构中的顺序表,熟练掌握顺序表中的各种函数接口。

        

②动态内存管理,熟悉掌握动态内存开辟的知识,正确使用malloc 、calloc、realloc等函数。

                

③为了方便代码的管理和保证通讯录实现逻辑的清晰性,我们将采用多文件管理的模式。

                

④熟练使用文件操作函数,能够正确读写文件。

          

⑤了解前置声明的知识。

                

如果有上面的一定基础就可以开始通讯录项目了~。 

      

1.1SeqList头文件的内容:

        

1.2Contact.h的头文件的内容:

        

二、通讯录的功能概要

       

对于一个基本的通讯录至少需要包含以下功能:

        

①⾄少能够存储100个⼈的通讯信息

        
②能够保存⽤⼾信息:名字、性别、年龄、电话、地址等

        
③增加联系⼈信息

        
④删除指定联系⼈

        
⑤查找制定联系⼈

        
⑥修改指定联系⼈

        
⑦显⽰联系⼈信息

        

⑧保存联系人数据不丢失

        

三、通讯录的实现

        

 3.1回顾顺序表的有关结构

基于上篇顺序表博客,我们通过以整形数组为主要结构,通过动态开辟的方法,实现了顺序表的各种方法,那么顺序表只能存储整形数据吗?

        

显然不是,我们可以将int类型的数据变换为char、结构体类型、自定义类型等,那么通讯录项目就是基于结构体类型的数据,顺序表中的每个元素均为结构体类型,如下图所示两者的对比与联系。   

        

 ①以整形为数据类型实现的顺序表,如下图所示:

        

        

②基于结构体实现的顺序表,我们也将该顺序表称作通讯录,其中顺序表每个元素都为结构体类型。

        

        

3.2SeqList.h 和 SeqList.c 简介

        

这里我们不过多讲述SeqList.h 和 SeqList.c实现,详情可以看顺序表博客,我们这里直接献上源码,想必帅观众看了顺序表博客后一定能够理解。        

           

3.3Contact.h 和 Contact.c 简介

①这里我们重点讲解Contact.c文件的实现

                

②如何处理多文件循环包含导致的问题

        

③调用底层顺序表以及写好的函数,实现代码的复用

        

四、Contact.h和Contact.c 实现        

4.1定义联系人结构体

#pragma once
//定义联系人数据
//包含: 姓名 性别 年龄 电话 地址
	
#define NAME_MAX  20
#define GENDER_MAX 10
#define TEL_MAX 20 
#define ADDR_MAX 100


typedef struct PersonInfo
{
	char name[NAME_MAX];
	char gender[GENDER_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
	int id;
}PeoInfo;

①这里我们通过定义宏变量,以便后续空间的合理修改。

        

②定义联系人数据的结构体,包含:姓名、性别、年龄、电话、地址、编号

        

③将结构体struct PersonInfo进行重命名PeoInfo

        

4.2顺序表重命名为通讯录(核心)

//因为在Contact.h中未包含SeqList.h的头文件,所以这里编译器不认识SL导致报错
//但能否添加SeqList的头文件呢?显然是不行的,这样会导致循环包含,无限展开而出现错误

//我们可以通过对SL进行前置声明,使编译器认识这个结构体,也就是顺序表

typedef struct SeqList Contact;

//在 C 语言中,typedef struct SeqList Contact; 
//这句话包含了前置声明的效果,但不等同于单纯的前置声明 
//它在完成前置声明的同时,还为结构体定义了一个别名。

①对于这里我们要思考这么一个问题:在底层的SeqList.h头文件已经包含了Contact.h头文件,我们能否在Contact.h头文件再次包含SeqList头文件呢?

        

这不同于头文件多次包含,可以使用#pragma once进行规避,这里是A文件包含B文件,同时B文件又包含A文件,导致文件的循环包含,在B文件打开A文件时,A文件又会打开B文件,导致循环展开,以至于无限循环。

        

②如果没有包含SeqList.h的头文件,我们又想使用去使用这个结构体变量,那又该如何做呢?

        

通过前置声明就可以做到这一点,在 C 语言中,typedef struct SeqList Contact;    它在完成前置声明的同时,还为结构体定义了一个别名,如果帅观众想要了解更多,可以去看一下前置声明的有关知识点,会对你加深这一块的理解。

        

③通过这段代码,我们将顺序表SeqList结构体重命名为Contact结构体,所以Contact结构体就相当于SeqList结构体,相当于土豆和马铃薯的区别,Contact结构体指针也可进行访问SeqList中的函数,这样就实现了调用底层顺序表SeqList封装的函数,以达到代码的复用,减少了代码的重复性。      

        

4.3通讯录初始化

Contact.h头文件声明:

//对通讯录进行初始化
void ContactInit (Contact*  con );

        

Contact.c文件实现:

//对通讯录进行初始化的实现
void ContactInit(Contact* con)
{
	//直接调用顺序表中的初始化操作
	SLInit(con);
}

直接调用顺序表中的初始化操作        

        

4.4通讯录的销毁

Contact.h头文件声明:

void ContactDesTroy(Contact* con);

        

Contact.c文件实现:

//对通讯录的销毁
void ContactDesTroy(Contact* con)
{
	SLDestroy(con);
}

直接调用顺序表中的销毁操作      

        

4.5通讯录添加数据

Contact.h头文件声明:

void ContactDel(Contact* con);

        

Contact.c文件实现:

//对通讯录添加数据
void ContactAdd(Contact* con)
{
	//先让用户输入数据
	//1.姓名 2.性别 3.年龄 4.电话 5.地址 

	//定义一个联系人结构体
	PeoInfo p = {0};
	
	//提示输入联系人的姓名
	printf("请输入联系人的姓名:\n");
	scanf("%s", p.name);
	
	printf("请输入联系人的性别:\n");
	scanf("%s", p.gender);

	printf("请输入联系人的年龄:\n");
	scanf("%d", &p.age);

	printf("请输入联系人的电话:\n");
	scanf("%s", &p.tel);

	printf("请输入联系人的地址:\n");
	scanf("%s", &p.addr);

	//通过尾插的方式,存入到通讯录中	
	SLPushBack(con, p);	

	//添加编号
	con->arr[con->size-1].id = con->size;

	//存入到文件中
	SaveContact(con);
	printf("存入成功!\n");
}

        

        这里我们通过第一个联系人数据的结构体,通过用户输入,将信息存入到该结构体中,然后通过顺序表尾插函数,将整个结构体的信息存入到通讯录中,并进行添加编号和存入文件操作。

        

4.6通讯录进行删除数据

Contact.h头文件声明:

void ContactDel(Contact* con);

        

Contact.c文件实现: 

int FindbyName(Contact* con , const char name[])
{
	//遍历整个顺序表
	for (int i = 0; i < con->size; i++)
	{
		//与名字进行匹配
		if (strcmp(con->arr[i].name, name) == 0)
		{
			//匹配成功返回其在顺序表中的位置
			return i;
		}
	}
	//未找到返回-1
	return -1;
}

void ContactDel(Contact* con)
{
	char name[NAME_MAX];
	printf("请输入你要删除的联系人姓名:\n");
	scanf("%s", name);
	int pos = FindbyName(con, name);
	if (pos == -1)
	{
		//未找到姓名,进行输出提示
		printf("未查找到该联系人!\n");	
	}	
	else
	{
		//找到了进行删除
		//通过调用顺序表中的指定位置删除函数
		SLErase(con, pos);
		printf("已删除该联系人\n");

		for (int i = pos; i < con->size; i++)
		{
			con->arr[i].id--;
		}

		//存入到文件中
		SaveContact(con);
	}

}

        

①通过定义了一个根据姓名查找的函数,如果能够查找到该联系人的姓名,则返回其对应在顺序表中的下标,如果查找不到该联系人的姓名则返回-1。

        

②根据查找到的联系人下标,通过调用顺序表中的指定位置删除函数,将联系人进行删除,并更新文件中存储的信息。

        

4.7通讯录查找数据

Contact.h头文件声明:

void ContactFind(Contact* con);

        

 Contact.c文件实现:  

void ContactFind(Contact* con)
{
	//提示输入要查找的联系人姓名
	printf("请输入要查找的联系人姓名:\n");
	char name[NAME_MAX] = {0};
	scanf("%s", name);
	int pos = FindbyName(con, name);
	if (pos == -1)
	{
		//提示为查找到联系人
		printf("查找失败,未查找到该联系人\n");
		//退出函数
		return;
	}

	//查找到联系人打印改联系人的信息
	printf("查找成功!\n");
	printf("%-6s %-10s %-10s %-10s %-10s %-10s \n", "编号", "姓名", "性别", "年龄", "电话", "地址");
	printf("%-6d %-10s %-10s %-10d %-10s %-10s\n",
		con->arr[pos].id,
		con->arr[pos].name,
		con->arr[pos].gender,
		con->arr[pos].age,
		con->arr[pos].tel,
		con->arr[pos].addr);

}

通过用户输入姓名进行查找,如果查找到了该联系人,进行展示该联系人的信息,如果未查找到联系人,直接退出函数,不进行返回操作。        

        

4.8通讯录进行修改数据

Contact.h头文件声明:

void ContactModify(Contact* con);

        

 Contact.c文件实现:  

void ContactModify(Contact* con)
{
	printf("请输入要修改的联系人姓名:\n");
	char name[NAME_MAX];
	scanf("%s", name);
	int pos = FindbyName(con, name);
	if (pos == -1)
	{
		//提示未找到联系人
		printf("修改失败,未找到该联系人!\n");
		return;
	}
	//找到联系人进行修改

	//提示输入联系人的姓名
	printf("请输入修改后联系人的姓名:\n");
	scanf("%s", con->arr[pos].name);

	printf("请输入修改后联系人的性别:\n");
	scanf("%s", con->arr[pos].gender);

	printf("请输入修改后联系人的年龄:\n");
	scanf("%d", &con->arr[pos].age);

	printf("请输入修改后联系人的电话:\n");
	scanf("%s", con->arr[pos].tel);

	printf("请输入修改后联系人的地址:\n");
	scanf("%s", con->arr[pos].addr);

	printf("成功修改!\n");

	//存入到文件中
	SaveContact(con);
}

①根据姓名进行查找通讯人,如果查找到该联系人,进行修改联系人的各种信息,如果未查找到该联系人,直接退出函数。

        

②直接将顺序表中指定位置的联系人数据进行覆盖

        

4.9展示通讯录的数据

Contact.h头文件声明:

void ContactShow(Contact* con);

        

 Contact.c文件实现:  

void ContactShow(Contact* con)
{
	printf("%-6s %-10s %-10s %-10s %-10s %-10s \n", "编号", "姓名", "性别", "年龄", "电话", "地址");
	for (int i = 0; i < con->size; i++)
	{
		printf("%-6d %-10s %-10s %-10d %-10s %-10s\n",
			con->arr[i].id,
			con->arr[i].name,
			con->arr[i].gender, 
			con->arr[i].age, 
			con->arr[i].tel,
			con->arr[i].addr);
	}

}

展示通讯录中的所有联系人信息。        

        

4.10存入通讯录数据到文件中

Contact.h头文件声明:

void SaveContact(Contact* con);

     

Contact.c文件实现:   

void SaveContact(Contact* con)
{
	FILE* pf = fopen("home.txt", "w");
	if (pf == NULL)
	{
		perror("fopen\n");
		return;
	}
	//输出到文件中

	for (int i = 0; i < con->size; i++)
	{
		fprintf(pf,"%-6s %-10s %-10s %-10s %-10s %-10s \n", "编号", "姓名", "性别", "年龄", "电话", "地址");

		fprintf(pf,"%-7d %-12s %-10s %-10d %-10s %-10s\n",
			con->arr[i].id,
			con->arr[i].name,
			con->arr[i].gender,
			con->arr[i].age,
			con->arr[i].tel,
			con->arr[i].addr);
	}
	fclose(pf);
	pf = NULL;

}

 通过文件操作函数fprintf将结构体按照指定格式化进行输出到文件中,保存联系人信息,防止数据丢失。       

        

4.11打印菜单

Contact.h头文件声明:


void menu();

        

Contact.c文件实现:   

enum
{
	exit,
	Add,
	Del,
	Find,
	Modify,
	Show,
};


void menu()
{
	int op = -1;
	Contact con;
	ContactInit(&con);
	do
	{	
		printf("********************************\n");
		printf("****1、添加用户  2、删除用户****\n");
		printf("****3、查找用户  4、修改用户****\n");
		printf("****5、展示用户  0、退出    ****\n");
		printf("********************************\n");

		printf("请输入您的操作:\n");
		//提示用户,请输入您的操作
		scanf("%d", &op);

		switch (op)
		{
		case Add:
			ContactAdd(&con);
			break;
		case Del:
			ContactDel(&con);
			break;
		case Find:
			ContactFind(&con);
			break;
		case Modify:
			ContactModify(&con);
			break;
		case Show:
			ContactShow(&con);
			break;
		case exit:
			printf("成功退出!\n");
			break;
		default:
			printf("输入有误,请重新输入\n");
		}

	} while (op != 0);

}

①通过枚举定义各个常量,将数字进行替换,增强代码的可读性      

        

②通过用户输入操作数,进行循环执行通讯录的增、删、改、查功能。  

        

五、项目演示

        

5.1添加和展示功能:

        

5.2查找功能:

      

5.3修改联系人:  

        

5.4删除联系人:

        

5.5退出程序:

        

六、项目源码

SeqList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Contact.h"
#pragma once
//动态顺序表的实现

typedef struct PersonInfo SLDataType;    
//通过将 struct PersonInfo 命名为SLDataType   以便后续更改类型 
	
//定义顺序表的结构
typedef struct SeqList
{
	SLDataType* arr;		//动态开辟顺序表

	int size;	//有效元素个数

	int capacity;		//顺序表的容量
}SL;
	

//顺序表初始化
void SLInit(SL* ps);
	
//顺序表销毁
void SLDestroy(SL* ps);
	
//顺序表扩容
void SLCheckCapacity(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);
	

        

SeqList.c

#include"SeqList.h"

//顺序表的初始化
	
//这里不用SL ps作为参数,因为形参是实参的临时拷贝,改变形参不影响实参
void SLInit(SL* ps)
{
	assert(ps);
	ps->arr = NULL;	
	ps->size = 0;	//默认顺序表有效个数为0
	ps->capacity = 0; //默认顺序表空间为0
}

//顺序表的销毁
void SLDestroy(SL* ps)
{
	//由于是动态开辟的,所以要进行free
	//判断是否为空,如果不为空将其free,并置为空
	assert(ps);
	if (ps->arr)
	{
		//不为空
		free(ps->arr);

		//此时ps->arr仍然保存着arr的地址
		//但我们不能够访问这片空间,此时改指针为野指针,我们需要将其置空处理
		ps->arr = NULL;
	}
	//有效个数清空
	ps->size = 0;

	//开辟的空间清0
	ps->capacity = 0;
}

	
//顺序表的扩容
void SLCheckCapacity(SL* ps)
{
	assert(ps);
	
	//什么时候应该扩容?空间不够用的时候。
	//什么时候空间不够用?当有效个数等于空间容量的时候。
	if (ps->size == ps->capacity)
	{
		//使用什么进行扩容? realloc调整动态开辟的空间
		//每次扩多大?根据数学推导,成倍数增加容量是最好的,一般为2~3倍最佳。

		//realloc(ps->arr,ps->capacity * 2 *sizeof(SLDataType) );
		//直接传入ps->capacity这样是正确的吗?
		//因为我们初始化为0,所以我们需要进行额外处理。

		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;  //若刚开始为0,则开辟4个空间
		SLDataType* tmp=(SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));	//防止空间开辟失败
		if (tmp == NULL)
		{
			perror("error");
			exit(1);
		}
		//开辟成功,用ps->arr进行维护
		ps->arr = tmp;
		
		//对临时变量进行置空
		tmp=NULL;

		//更新当前空间
		ps->capacity = newCapacity;
	}
}
	
//顺序表的尾插
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);

	//判断是否空间足够
	SLCheckCapacity(ps);

	ps->arr[ps->size++] = x;

}
	
//顺序表的头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);

	//判断空间是否足够
	SLCheckCapacity(ps);

	for (int i = ps->size; i>0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];    //根据最后一个元素移动,确定判断条件    arr[1]=arr[0]
	}
	//移动结束后,将元素插入第一个位置,即下标为0处。
	ps->arr[0] = x;
	ps->size++;
}
	
//顺序表的尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	//判断是否可以进行删除
	assert(ps->size > 0);

	//将size个数减少一个即可,删除的元素可以被覆盖
	ps->size--;
}
		
void SLPopFront(SL* ps)
{
	assert(ps);
	//判断是否可以进行删除
	assert(ps->size > 0);
	for (int i = 0; i<ps->size-1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];  //根据最后一个元素移动,确定判断条件    arr[ps->size-2]=arr[ps->size-1]
	}
	//删除一个元素,ps->size的有效个数要减少
	ps->size--;
}
	
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	//判断pos是否在有效的位置(size下标可以插入一个元素)
	assert(pos >= 0 && pos <= ps->size);
	//判断空间是否足够
	SLCheckCapacity(ps);
	for (int i = ps->size; i>pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];	//根据最后一个元素移动,确定判断条件    arr[pos+1]=arr[pos];
	}
	ps->arr[pos] = x;
	//增添一个元素,有效个数要增加1
	ps->size++;
}
	
void SLErase(SL* ps, int pos)
{
	assert(ps);
	//判断pos是否在有效的位置 (size下标没有元素可删除)
	assert(pos >= 0 && pos < ps->size);
	assert(ps->size > 0);
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];  //根据最后一个元素移动,确定判断条件  arr[size-2]=arr[size-1]
	}
	//删除一个元素,有效个数要减少一个
	ps->size--;
}

        

Contact.h

#pragma once
//定义联系人数据
//包含: 姓名 性别 年龄 电话 地址
	
#define NAME_MAX  20
#define GENDER_MAX 10
#define TEL_MAX 20 
#define ADDR_MAX 100


typedef struct PersonInfo
{
	char name[NAME_MAX];
	char gender[GENDER_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
	int id;
}PeoInfo;
	

//因为在Contact.h中未包含SeqList.h的头文件,所以这里编译器不认识SL导致报错
//但能否添加SeqList的头文件呢?显然是不行的,这样会导致循环包含,无限展开而出现错误

//我们可以通过对SL进行前置声明,使编译器认识这个结构体,也就是顺序表


struct SeqList;
typedef struct SeqList Contact;

//在 C 语言中,typedef struct SeqList Contact; 这句话包含了前置声明的效果,但不等同于单纯的前置声明 
// —— 它在完成前置声明的同时,还为结构体定义了一个别名。

	
//对通讯录进行初始化
void ContactInit (Contact*  con );
	
//对通讯录的销毁
void ContactDesTroy(Contact* con);
		
//对通讯录添加数据
void ContactAdd(Contact* con);
	
//对通讯录进行删除数据
void ContactDel(Contact* con);

//对通讯录进行查找数据
void ContactFind(Contact* con);

//对通讯录进行修改数据
void ContactModify(Contact* con);
	
//展示通讯录的数据
void ContactShow(Contact* con);
	
//存入通讯录数据到文件中
void SaveContact(Contact* con);

void menu();

        

Contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"		//---包含了"Contact.h"的头文件
#include<string.h>
//对通讯录进行初始化的实现
void ContactInit(Contact* con)
{
	//直接调用顺序表中的初始化操作
	SLInit(con);
}

//对通讯录的销毁
void ContactDesTroy(Contact* con)
{
	SLDestroy(con);
}

//对通讯录添加数据
void ContactAdd(Contact* con)
{
	//先让用户输入数据
	//1.姓名 2.性别 3.年龄 4.电话 5.地址 

	//定义一个联系人结构体
	PeoInfo p = {0};
	
	//提示输入联系人的姓名
	printf("请输入联系人的姓名:\n");
	scanf("%s", p.name);
	
	printf("请输入联系人的性别:\n");
	scanf("%s", p.gender);

	printf("请输入联系人的年龄:\n");
	scanf("%d", &p.age);

	printf("请输入联系人的电话:\n");
	scanf("%s", &p.tel);

	printf("请输入联系人的地址:\n");
	scanf("%s", &p.addr);

	//通过尾插的方式,存入到通讯录中	
	SLPushBack(con, p);	

	//添加编号
	con->arr[con->size-1].id = con->size;

	//存入到文件中
	SaveContact(con);
	printf("存入成功!\n");
}

//删除通讯录的联系人
 
//方法一:根据姓名查找  

int FindbyName(Contact* con , const char name[])
{
	//遍历整个顺序表
	for (int i = 0; i < con->size; i++)
	{
		//与名字进行匹配
		if (strcmp(con->arr[i].name, name) == 0)
		{
			//匹配成功返回其在顺序表中的位置
			return i;
		}
	}
	//未找到返回-1
	return -1;
}

	
void ContactDel(Contact* con)
{
	char name[NAME_MAX];
	printf("请输入你要删除的联系人姓名:\n");
	scanf("%s", name);
	int pos = FindbyName(con, name);
	if (pos == -1)
	{
		//未找到姓名,进行输出提示
		printf("未查找到该联系人!\n");	
	}	
	else
	{
		//找到了进行删除
		//通过调用顺序表中的指定位置删除函数
		SLErase(con, pos);
		printf("已删除该联系人\n");

		for (int i = pos; i < con->size; i++)
		{
			con->arr[i].id--;
		}

		//存入到文件中
		SaveContact(con);
	}

}

	
////方法二:根据编号查找
//int FindbyId(Contact* con, int id)
//{
//	//遍历整个顺序表
//	for (int i = 0; i < con->size; i++)
//	{
//		//根据id进行匹配
//		if (con->arr[i].id == id)
//		{
//			//匹配成功返回其在顺序表中的位置
//			return i;
//		}
//	}
//	//未找到返回-1
//	return -1;
//}
//	
//void ContactDel(Contact* con)
//{
//	int id = -1;
//	printf("请输入你要删除的联系人的编号:\n");
//	scanf("%d", &id);
//	int pos = FindbyId(con, id);
//	if (pos == -1)
//	{
//		//未找到姓名,进行输出提示
//		printf("未查找到该联系人!");
//	}
//	else
//	{
//		//找到了进行删除
//		//通过调用顺序表中的指定位置删除函数
//		SLErase(con, pos);
//		printf("已删除该联系人");
//	}
//
//}


void ContactShow(Contact* con)
{
	printf("%-6s %-10s %-10s %-10s %-10s %-10s \n", "编号", "姓名", "性别", "年龄", "电话", "地址");
	for (int i = 0; i < con->size; i++)
	{
		printf("%-6d %-10s %-10s %-10d %-10s %-10s\n",
			con->arr[i].id,
			con->arr[i].name,
			con->arr[i].gender, 
			con->arr[i].age, 
			con->arr[i].tel,
			con->arr[i].addr);
	}

}


void ContactFind(Contact* con)
{
	//提示输入要查找的联系人姓名
	printf("请输入要查找的联系人姓名:\n");
	char name[NAME_MAX] = {0};
	scanf("%s", name);
	int pos = FindbyName(con, name);
	if (pos == -1)
	{
		//提示为查找到联系人
		printf("查找失败,未查找到该联系人\n");
		//退出函数
		return;
	}

	//查找到联系人打印改联系人的信息
	printf("查找成功!\n");
	printf("%-6s %-10s %-10s %-10s %-10s %-10s \n", "编号", "姓名", "性别", "年龄", "电话", "地址");
	printf("%-6d %-10s %-10s %-10d %-10s %-10s\n",
		con->arr[pos].id,
		con->arr[pos].name,
		con->arr[pos].gender,
		con->arr[pos].age,
		con->arr[pos].tel,
		con->arr[pos].addr);

}

//根据姓名修改通讯录中的数据
//对通讯录进行修改数据
void ContactModify(Contact* con)
{
	printf("请输入要修改的联系人姓名:\n");
	char name[NAME_MAX];
	scanf("%s", name);
	int pos = FindbyName(con, name);
	if (pos == -1)
	{
		//提示未找到联系人
		printf("修改失败,未找到该联系人!\n");
		return;
	}
	//找到联系人进行修改

	//提示输入联系人的姓名
	printf("请输入修改后联系人的姓名:\n");
	scanf("%s", con->arr[pos].name);

	printf("请输入修改后联系人的性别:\n");
	scanf("%s", con->arr[pos].gender);

	printf("请输入修改后联系人的年龄:\n");
	scanf("%d", &con->arr[pos].age);

	printf("请输入修改后联系人的电话:\n");
	scanf("%s", con->arr[pos].tel);

	printf("请输入修改后联系人的地址:\n");
	scanf("%s", con->arr[pos].addr);

	printf("成功修改!\n");

	//存入到文件中
	SaveContact(con);
}


void SaveContact(Contact* con)
{
	FILE* pf = fopen("home.txt", "w");
	if (pf == NULL)
	{
		perror("fopen\n");
		return;
	}
	//输出到文件中

	for (int i = 0; i < con->size; i++)
	{
		fprintf(pf,"%-6s %-10s %-10s %-10s %-10s %-10s \n", "编号", "姓名", "性别", "年龄", "电话", "地址");

		fprintf(pf,"%-7d %-12s %-10s %-10d %-10s %-10s\n",
			con->arr[i].id,
			con->arr[i].name,
			con->arr[i].gender,
			con->arr[i].age,
			con->arr[i].tel,
			con->arr[i].addr);
	}
	fclose(pf);
	pf = NULL;

}


enum
{
	quit,
	Add,
	Del,
	Find,
	Modify,
	Show,
};



void menu()
{
	int op = -1;
	Contact con;
	ContactInit(&con);
	do
	{	
		printf("********************************\n");
		printf("****1、添加用户  2、删除用户****\n");
		printf("****3、查找用户  4、修改用户****\n");
		printf("****5、展示用户  0、退出    ****\n");
		printf("********************************\n");

		printf("请输入您的操作:\n");
		//提示用户,请输入您的操作
		scanf("%d", &op);

		switch (op)
		{
		case Add:
			ContactAdd(&con);
			break;
		case Del:
			ContactDel(&con);
			break;
		case Find:
			ContactFind(&con);
			break;
		case Modify:
			ContactModify(&con);
			break;
		case Show:
			ContactShow(&con);
			break;
		case quit:
			printf("成功退出!\n");
			break;
		default:
			printf("输入有误,请重新输入\n");
		}

	} while (op != 0);

}

        

Test.c

#include"SeqList.h"

int main()
{
	menu();
	return 0;
}

        

七、总结

通过改通讯录项目,我们能够更好的了解动态内存管理、多文件编程、模块化编程、调试、前置声明、函数复用等技能。

        

既然看到这里了,不妨点赞+收藏,感谢大家,若有问题请指正。

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/hii_echo/article/details/151590371

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--