1. 多文件操作
SeqList.h //用于定义顺序表的结构,增删查改等函数的声明 SeqList.c //用于实现增删查改等函数 Contact.h //用于定义通讯录的结构,通讯录中联系人的增删查改等函数的声明 Contact.c //用于实现通讯录中增删查改等函数 Test.h //用于测试上述实现的函数,和菜单界面
2. 通讯录的主要功能
1.初始化通讯录
2.销毁通讯录
3.展示联系人数据
4.增加联系人数据
5.判断指定联系人是否存在
6.删除联系人数据
7.查找联系人数据
8.修改联系人数据
对通讯录中各个功能的展示:
3. 通讯录的实现
想要基于顺序表实现通讯录,首先我们需要一个顺序表。由于顺序表的实现比较简单,这里直接给出,不进行详细说明 (如想要详细了解顺序表的实现,请前往我的主页查看)。
说明:下面两个文件中注释的部分是在顺序表类型替换后(由整型替换为结构体类型)的不适用的部分。读者不必在意。
SeqList.h
在这个头文件中由于要用上Contact.h中定义的peoInfo,所以要包含Contact.h。
#pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "Contact.h" //typedef int SQDataType; typedef peoInfo SQDataType; typedef struct SeqList { SQDataType* arr; int size; int capacity; }SL; void SLInit(SL* ps); void SLDestory(SL* ps); //void SLPrint(SL ps); void SLPushBack(SL* ps, SQDataType x); void SLPushFront(SL* ps, SQDataType x); void SLPopBack(SL* ps); void SLPopFront(SL* ps); //void SLFind(const SL* ps, SQDataType x); void SLInsert(SL* ps, int pos, SQDataType x); void SLErase(SL* ps, int pos);
SeqList.c
#define _CRT_SECURE_NO_WARNINGS #include "SeqList.h" void SLInit(SL* ps) { ps->arr = (SQDataType*)malloc(sizeof(SQDataType) * 4); if (ps->arr == NULL) { perror("malloc fail!\n"); return ; } ps->capacity = 4; ps->size = 0; } void SLDestory(SL* ps) { free(ps->arr); ps->arr = NULL; ps->capacity = 0; ps->size = 0; } void CheckCapacity(SL* ps,SQDataType x) { if (ps->size == ps->capacity) { SQDataType* tmp = (SQDataType*)realloc(ps->arr, sizeof(SQDataType) * ps->capacity * 2); if (tmp == NULL) { perror("realloc fail!\n"); return ; } else { ps->arr = tmp; ps->capacity *= 2; } } } //void SLPrint(SL ps) //{ // for (int i = 0; i < ps.size; i++) // { // printf("%d ", ps.arr[i]); // } // printf("\n"); // //} void SLPushBack(SL* ps, SQDataType x) { assert(ps); CheckCapacity(ps, x); ps->arr[ps->size] = x; ps->size++; } void SLPushFront(SL* ps, SQDataType x) { assert(ps); CheckCapacity(ps, x); int i = ps->size; for (i = ps->size; i > 0; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[i] = x; ps->size++; } void SLPopBack(SL* ps) { assert(ps); if (ps->size == 0) { printf("无数据可删除!\n"); return; } ps->size--; } void SLPopFront(SL* ps) { assert(ps); if (ps->size == 0) { printf("无数据可删除!\n"); return; } for (int i = 0; i < ps->size; i++) { ps->arr[i] = ps->arr[i + 1]; } ps->size--; } //void SLFind(const SL* ps, SQDataType x) //{ // assert(ps); // if (ps->size == 0) // { // printf("无数据!\n"); // return; // } // // for (int i = 0; i < ps->size; i++) // { // if (ps->arr[i] == x) // { // printf("找到了\n"); // return; // } // } // // printf("找不到\n"); //} void SLInsert(SL* ps, int pos, SQDataType x) { assert(ps && pos < ps->size && ps->size>0); CheckCapacity( ps, x); int i = ps->size; for ( i = ps->size; i > pos; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[i] = x; ps->size++; } void SLErase(SL* ps, int pos) { assert(pos < ps->size && ps->size>0); for (int i = pos; i < ps->size; i++) { ps->arr[i] = ps->arr[i + 1]; } ps->size--; }
1.定义通讯录的结构
由于我们这里需要用到SeqList.h中的struct SeqList结构体,很多人觉得只要在Contact .h头文件中包含SeqList.h就行,其实这是错误的,因为在SeqList.h中已经包含了Contact .h,头文件是不能交叉包含的。
所以我们这里用了另一个方法,叫做前置声明,前置声明的意思是告诉这个头文件我们存在这个顺序表,并且可以使用。
要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作。
有了前置声明,下面的 Contact * con 就是顺序表中的 SL*sl。
#pragma once #include <stdio.h> #include <string.h> #define MAX_NAME 10 #define MAX_GENDER 10 #define MAX_TEL 20 #define MAX_ADDR 20 //定义联系人数据结构:姓名+性别+年龄+电话+地址 typedef struct PeosonInfo { char name[MAX_NAME]; char gender[MAX_GENDER]; int age; char tel[MAX_TEL]; char addr[MAX_ADDR]; }peoInfo; //前置声明 typedef struct SeqList Contact; //给顺序表改个名字,叫做通讯录 //注意:这里不能写做typedef SL Contact,而是要用原名。 //因为SL是在struct SeqList定义好了之后才重命名的 //要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作 //有了前置声明,下面的 Contact* con 就是顺序表中的 SL*sl
2.初始化通讯录
void InitContact(Contact* con)//就是相当于SL* sl { //实际上就是顺序表的初始化 SLInit(con); }
3.销毁通讯录
void DestroyContact(Contact* con) { SLDestory(con); }
4.展示联系人数据
展示联系人不能改变其数据内容,所以可以加const进行保护。
void ShowContact(const Contact* con) { //表头:姓名+性别+年龄+电话+地址 printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址"); for (int i = 0; i < con->size; i++) { printf("%-5s %-5s %-5d %-5s %-5s\n", con->arr[i].name, con->arr[i].gender, con->arr[i].age, con->arr[i].tel, con->arr[i].addr); } printf("\n"); }
5.增加联系人数据
增加通讯录中联系人的数据,实际上就是对顺序表的插入操作,可以使用头插和尾插。这里是尾插。
void AddContact(Contact* con) { peoInfo info;//定义一个结构体对象 printf("请输入要添加联系人的姓名:\n"); scanf("%s", info.name); printf("请输入要添加联系人的性别:\n"); scanf("%s", info.gender); printf("请输入要添加联系人的年龄:\n"); scanf("%d", &(info.age)); printf("请输入要添加联系人的电话:\n"); scanf("%s", info.tel); printf("请输入要添加联系人的地址:\n"); scanf("%s", info.addr); //往通讯录中添加联系人数据 SLPushBack(con, info);//与顺序表中尾插函数的参数对应 }
6.判断指定联系人是否存在
这个函数是在删除联系人,查找联系人和修改联系人中使用的,因为这三个操作都要先判断该联系人是否存在。若存在,则返回该联系人的位置下标,若不存在则返回-1。
int FindByname(const Contact* con, char name[]) { for (int i = 0; i < con->size; i++) { if (strcmp(con->arr[i].name, name) == 0) { //找到了 return i; } } //没有找到 return -1; }
7.删除联系人数据
删除之前要判断这个人是否存在通讯录中,如果这个人存在,说明我们知道他的位置,直接复用顺序表中的对应函数进行删除。
void DelContact(Contact* con) { //1.判断要删除的人是否存在 char name[MAX_NAME]; printf("请输入要删除人的姓名:\n"); scanf("%s",name); int find = FindByname(con, name); if (find < 0) { printf("要删除的联系人不存在!\n"); return; } //2.删除:知道了要删除的联系人数据对应的下标 SLErase(con, find); printf("删除成功!\n"); printf("\n"); }
8.查找联系人数据
在查找之前也要先判断该联系人是否存在,若存在,则打印出该联系人的全部信息;若不存在,则进行提示。
void FindContact(const Contact* con) { char name[MAX_NAME]; printf("请输入要查找的人的姓名:\n"); scanf("%s", name); int find = FindByname(con, name); if (find < 0) { printf("要查找的人不存在!\n"); return; } printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%-5s %-5s %-5d %-5s %-5s\n", con->arr[find].name, con->arr[find].gender, con->arr[find].age, con->arr[find].tel, con->arr[find].addr); printf("\n"); }
9.修改联系人数据
修改联系人之前也要判断此人是否存在,存在才修改,不存在则进行提示。
void ModifyContact(Contact* con) { //1.判断要修改的联系人是否存在 char name[MAX_NAME]; printf("请输入要修改人的姓名:\n"); scanf("%s", name); int find = FindByname(con, name); if (find < 0) { printf("要修改的联系人不存在!\n"); return; } //联系人存在,直接修改指定下标的联系人信息 printf("请输入新的联系人的姓名:\n"); scanf("%s", con->arr[find].name); printf("请输入新的联系人的性别:\n"); scanf("%s", con->arr[find].gender); printf("请输入新的联系人的年龄:\n"); scanf("%d", &(con->arr[find].age)); printf("请输入新的联系人的电话:\n"); scanf("%s", con->arr[find].tel); printf("请输入新的联系人的地址:\n"); scanf("%s", con->arr[find].addr); printf("修改成功!\n"); printf("\n"); }
4. 通讯录完整代码
Contact.h
#pragma once #include <stdio.h> #include <string.h> #define MAX_NAME 10 #define MAX_GENDER 10 #define MAX_TEL 20 #define MAX_ADDR 20 //定义联系人数据结构:姓名+性别+年龄+电话+地址 typedef struct PeosonInfo { char name[MAX_NAME]; char gender[MAX_GENDER]; int age; char tel[MAX_TEL]; char addr[MAX_ADDR]; }peoInfo; //前置声明 typedef struct SeqList Contact; //给通讯录改个名字,叫做通讯录 //注意:这里不能写做typedef SL Contact,而是要用原名。 //因为SL是在struct SeqList定义好了之后才重命名的 //要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作 //有了前置声明,下面的 Contact* con 就是顺序表中的 SL*sl //初始化通讯录 void InitContact(Contact* con); //添加通讯录数据 void AddContact(Contact* con); //删除通讯录数据 void DelContact(Contact* con); //展示通讯录数据 void ShowContact(const Contact* con); //查找通讯录数据 void FindContact(const Contact* con); //修改通讯录数据 void ModifyContact(Contact* con); //销毁通讯录数据 void DestroyContact(Contact* con);
Contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "Contact.h" #include "SeqList.h" //初始化通讯录 void InitContact(Contact* con)//就是相当于SL* sl { //实际上就是顺序表的初始化 SLInit(con); } //销毁通讯录数据 void DestroyContact(Contact* con) { SLDestory(con); } //添加通讯录数据 void AddContact(Contact* con) { peoInfo info; printf("请输入要添加联系人的姓名:\n"); scanf("%s", info.name); printf("请输入要添加联系人的性别:\n"); scanf("%s", info.gender); printf("请输入要添加联系人的年龄:\n"); scanf("%d", &(info.age)); printf("请输入要添加联系人的电话:\n"); scanf("%s", info.tel); printf("请输入要添加联系人的地址:\n"); scanf("%s", info.addr); //往通讯录中添加联系人数据 SLPushBack(con, info);//与顺序表中尾插函数的参数对应 } //展示通讯录数据 void ShowContact(const Contact* con) { //表头:姓名+性别+年龄+电话+地址 printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址"); for (int i = 0; i < con->size; i++) { printf("%-5s %-5s %-5d %-5s %-5s\n", con->arr[i].name, con->arr[i].gender, con->arr[i].age, con->arr[i].tel, con->arr[i].addr); } printf("\n"); } int FindByname(const Contact* con, char name[]) { for (int i = 0; i < con->size; i++) { if (strcmp(con->arr[i].name, name) == 0) { //找到了 return i; } } //没有找到 return -1; } //删除指定位置通讯录数据 void DelContact(Contact* con) { //1.判断要删除的人是否存在 char name[MAX_NAME]; printf("请输入要删除人的姓名:\n"); scanf("%s",name); int find = FindByname(con, name); if (find < 0) { printf("要删除的联系人不存在!\n"); return; } //2.删除:知道了要删除的联系人数据对应的下标 SLErase(con, find); printf("删除成功!\n"); printf("\n"); } //修改通讯录数据 void ModifyContact(Contact* con) { //1.判断要修改的联系人是否存在 char name[MAX_NAME]; printf("请输入要修改人的姓名:\n"); scanf("%s", name); int find = FindByname(con, name); if (find < 0) { printf("要修改的联系人不存在!\n"); return; } //联系人存在,直接修改指定下标的联系人信息 printf("请输入新的联系人的姓名:\n"); scanf("%s", con->arr[find].name); printf("请输入新的联系人的性别:\n"); scanf("%s", con->arr[find].gender); printf("请输入新的联系人的年龄:\n"); scanf("%d", &(con->arr[find].age)); printf("请输入新的联系人的电话:\n"); scanf("%s", con->arr[find].tel); printf("请输入新的联系人的地址:\n"); scanf("%s", con->arr[find].addr); printf("修改成功!\n"); printf("\n"); } //查找通讯录数据 void FindContact(const Contact* con) { char name[MAX_NAME]; printf("请输入要查找的人的姓名:\n"); scanf("%s", name); int find = FindByname(con, name); if (find < 0) { printf("要查找的人不存在!\n"); return; } printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%-5s %-5s %-5d %-5s %-5s\n", con->arr[find].name, con->arr[find].gender, con->arr[find].age, con->arr[find].tel, con->arr[find].addr); printf("\n"); }
Test.c
void menu() { printf("************** 通讯录 *************\n"); printf("******* 1.添加联系人 2.删除联系人 *******\n"); printf("******* 3.查找联系人 4.修改联系人 *******\n"); printf("******* 5.展示联系人 0.退出通讯录 *****\n"); printf("******************************************\n"); } enum option { Exit, addcontact, delcontact, findcontact, modifycontact, showcontact }; int main() { //TestSeqList(); //ContactTest(); Contact con; InitContact(&con); int input = 1; do { menu(); printf("请选择:"); scanf("%d", &input); switch (input) { case Exit: printf("退出通讯录!\n"); break; case addcontact: AddContact(&con); break; case delcontact: DelContact(&con); break; case findcontact: FindContact(&con); break; case modifycontact: ModifyContact(&con); break; case showcontact: ShowContact(&con); break; default: printf("选择错误,请重新选择!\n"); break; } } while (input); return 0; }