前言:
以下是一个基于 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