本节应用是要用顺序表实现一个通讯录,收录联系人的姓名、性别、电话号码、住址、年龄
顺序表的实现在上一节中已经完成了,本节的任务其实就是应用上节写出来的代码的那些接口函数功能,做出来一个好看的,可视化的东西
首先把准备工作做好,创立好通讯录的头文件Contest.h和源文件Contest.c还有测试源文件,再把上一节的顺序表文件链接过来,在这节中直接使用上节的函数功能
1. 通讯录数据类型
首先我们在Contect.h文件中把通讯录结构体写出来,再改一个好写的名字
这边数组长度是用宏定义的,为了方便以后更改
2. 通讯录操作方法
现在我们写一下通讯录的操作发方法,跟之前的写法一样,写一个菜单函数,然后用do···while和switch语句让用户选择要进行什么操作
在Contect.c文件中写menu()函数,并包含上它的头文件Contect.h
用户操作方案,写在test.c中:
3. 顺序表改成通讯录
现在我们要将上节的顺序表稍加修改,将arr中的元素从int型改成通讯录结构体型的,此时,需要在SeqList.h中包含上Contect.h
修改一下SLDatatype的表示对象为Info就完成了
4. 通讯录里提供的操作
这个时候我们需要用到SeqList文件中的内容了,但是我们不能在Contest.h中引用SeqList.h了,这样会导致头文件嵌套问题,你包含我,我包含你,没完没了,程序就出错了,所以我们的头文件包含要定好一个顺序,就像这样
确定好这个顺序了,那我们如何使用SeqList.h文件中的内容呢,此时的解决方法就是前置声明,因为内容在后面,但是我们要在前面使用它,所以我们可以在前面声明一下这个内容,就可以在前面使用它了
前置声明之后再改个名字,从顺序表改成通讯录,方便后边辨识和使用
这时我们运行一下会发现喜提一大堆错误,这时不要慌,问题就出现在之前顺序表的查找和打印函数上了,它们的类型错误导致的报错,我们只需要把它们注释掉就可以了
4.1 通讯录的初始化和销毁
这块功能非常简单,我们只需要复用SeqList.h中的函数就可以
然后在测试文件中调试一下
发现没问题,该创建的东西都创建好了
4.2 "增""删""查""改""查看通讯录"
4.2.1 "增"
首先创建一个临时变量info暂时存放一个人的所有信息,然后复用函数就可以了
在把增的功能添加到主函数之前,我们要在do···while外边写好创建、初始化和销毁通讯录顺序表的功能
然后把ContectAdd()函数放到"增"的位置就好了
4.2.2 "查看通讯录"
先写查看通讯录用来方便检验之后的代码
然后把这个功能加到main函数里头
4.2.3 "查"
查找的话可以通过那5个方面去查,这里我就写一个通过名字查的
这个FindByName()函数因为后面还要用,所以就单拎出来写的,注意用到了字符串操作函数,要引用一下头文件。
4.2.4 "删"
4.2.5 "改"
5. 完整代码
Contect.h完整代码
//通讯录数据类型
#define NAME_MAX 100
#define GENDER_MAX 10
#define TEL_MAX 12
#define ADDR_MAX 100
typedef struct PersonInfo
{
char name[NAME_MAX];
char gender[GENDER_MAX];
char tel[TEL_MAX];
char addr[ADDR_MAX];
int age;
}Info;
//通讯录菜单
void menu();
//使用顺序表的前置声明
struct SeqList;
typedef struct SeqList Contect;
//通讯录里提供的操作
//通讯录的初始化和销毁
void ContectInit(Contect* pcon);
void ContectDestory(Contect* pcon);
//增、删、改、查、查看通讯录
//增
void ContectAdd(Contect* pcon);
//查看通讯录
void ContectShow(Contect* pcon);
//查
void ContectFind(Contect* pcon);
//删
void ContectDel(Contect* pcon);
//改
void ContectModify(Contect* pcon);
Contect.c完整代码
#include"SeqList.h"
//通讯录菜单
void menu()
{
printf("******************通讯录*******************\n");
printf("*********1.添加联系人 2.删除联系人*********\n");
printf("*********3.修改联系人 4.查找联系人*********\n");
printf("*********5.查看通讯录 0.退出通讯录*********\n");
printf("*******************************************\n");
}
//通讯录里提供的操作
//通讯录的初始化和销毁
void ContectInit(Contect* pcon)
{
SLInit(pcon);
}
void ContectDestory(Contect* pcon)
{
SLDestory(pcon);
}
//增、删、改、查、查看通讯录
//增
void ContectAdd(Contect* pcon)
{
//创建一个通讯录结构体用来临时存放一个人的所有信息
Info info;
printf("请输入联系人姓名:>");
scanf("%s", info.name);
printf("请输入联系人性别:>");
scanf("%s", info.gender);
printf("请输入联系人电话:>");
scanf("%s", info.tel);
printf("请输入联系人地址:>");
scanf("%s", info.addr);
printf("请输入联系人年龄:>");
scanf("%d", &info.age);
//保存到通讯录顺序表中
SLPushBack(pcon, info);
}
//查看通讯录
void ContectShow(Contect* pcon)
{
for (int i = 0; i < pcon->size; i++)
{
printf("-------------------------------\n");
printf("姓名:%s\n", pcon->arr[i].name);
printf("性别:%s\n", pcon->arr[i].gender);
printf("电话:%s\n", pcon->arr[i].tel);
printf("地址:%s\n", pcon->arr[i].addr);
printf("年龄:%d\n", pcon->arr[i].age);
}
}
//查
#include<string.h>
int FindByName(Contect* pcon, char name[])
{
for (int i = 0; i < pcon->size; i++)
{
if (strcmp(pcon->arr[i].name, name) == 0)
{
//找到了
return i;
}
}
//没找到
return -1;
}
void ContectFind(Contect* pcon)
{
char name[NAME_MAX];
printf("请输入要查找的人名:>");
scanf("%s", name);
int ret = FindByName(pcon, name);
if (ret < 0)
{
printf("要查找的联系人不存在!\n");
return;
}
//找到了,打印一下查找的联系人的信息
printf("-------------------------------\n");
printf("姓名:%s\n", pcon->arr[ret].name);
printf("性别:%s\n", pcon->arr[ret].gender);
printf("电话:%s\n", pcon->arr[ret].tel);
printf("地址:%s\n", pcon->arr[ret].addr);
printf("年龄:%d\n", pcon->arr[ret].age);
}
//删
void ContectDel(Contect* pcon)
{
//删除前先查找,找到了可以删,找不到不能删
printf("请输入要删除的联系人姓名:>");
char name[NAME_MAX];
scanf("%s", name);
int ret = FindByName(pcon, name);
if (ret < 0)
{
printf("要删除的联系人不存在!\n");
return;
}
//执行删除操作
SLErase(pcon, ret);
printf("删除成功\n");
}
//改
void ContectModify(Contect* pcon)
{
//修改前先查找,找到了改,找不到不能改
printf("请输入要修改的联系人姓名:>");
char name[NAME_MAX];
scanf("%s", name);
int ret = FindByName(pcon, name);
if (ret < 0)
{
printf("要修改的联系人不存在!\n");
return;
}
//执行修改操作
printf("开始修改!\n");
printf("请输入姓名:>");
scanf("%s", pcon->arr[ret].name);
printf("请输入性别:>");
scanf("%s", pcon->arr[ret].gender);
printf("请输入电话:>");
scanf("%s", pcon->arr[ret].tel);
printf("请输入地址:>");
scanf("%s", pcon->arr[ret].addr);
printf("请输入年龄:>");
scanf("%d", &pcon->arr[ret].age);
printf("联系人修改成功!\n");
}
SeqList.h完整代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Contect.h"
typedef Info SLDatatype;
typedef struct SeqList
{
SLDatatype* arr; //存储数据的底层结构
int capacity; //记录顺序表的空间大小
int size; //有效数据个数
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestory(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);
//在顺序表中把pos位置的数据改成x
void SLChange(SL* ps, int pos, SLDatatype x);
SeqList.c完整代码
#include"SeqList.h"
//初始化和销毁
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
void SLDestory(SL* ps)
{
assert(ps);
if (ps->arr)//arr不是空的再释放,也可以不判断
{
free(ps->arr);//free空指针函数什么都不会做
}
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
//void SLPrint(SL* ps)
//{
// assert(ps);
// for (int i = 0; i < ps->size; i++)
// {
// printf("%d ", ps->arr[i]);
// }
// printf("\n");
//}
//顺序表插入数据
//判断空间是否足够,不够就扩容
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)//空间不够时
{
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//为了防止扩容失败导致数据丢失,扩容后的空间地址先不给ps->arr
SLDatatype* tmp = (SLDatatype*)realloc(ps->arr, newCapacity * sizeof(SLDatatype));
if (tmp == NULL)//扩容失败
{
printf("realloc fail!\n");
exit(1);//错误退出码1
}
//扩容成功
ps->arr = tmp;
ps->capacity = newCapacity;
}
}
//从尾部插入
//逻辑:空间足够就直接尾插,空间不够就扩容,扩容一般是扩容当前空间的2倍
void SLPushBack(SL* ps, SLDatatype x)
{
assert(ps);
//判断空间够不够,不够就扩容
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--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
//在添加完元素之后一定要记得 ps->size 增加一个
//顺序表删除数据
//从尾部删除
//逻辑:顺序表为空,不能执行删除,顺序表不为空直接删最后一个数据
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);//数据为空报警
//顺序表不为空
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--;
}
//在删除完数据之后也要记得减size
//顺序表任意位置增删数据
//指定位置前面增加数据
void SLInsert(SL* ps, int pos, SLDatatype x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
//pos及之后的数据往后挪一位
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
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->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;//没找到返回-1
//}
//在顺序表中把pos位置的数据改成x
void SLChange(SL* ps, int pos, SLDatatype x)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
ps->arr[pos] = x;
}
最后,这套代码是用完一次之后里面存的联系人信息就没了,这时我们可以借助文件操作函数,将数据保存下来,这样下次打开的时候还能加载出来
C语言·文件操作-CSDN博客文章浏览阅读923次,点赞24次,收藏21次。本节介绍了文件的用处,如何用文件指针打开和关闭文件,流的读写函数fgetc fputc fgets fputs fscanf fprintf fread fwrite,和字符串的的格式化输入输出函数sscanf sprintf控制文件指针实现随机读写的函数fseek ftell rewind,文件结束相关函数feof ferror,文件缓冲区的概念https://blog.csdn.net/atlanteep/article/details/134894644?spm=1001.2014.3001.5501