前言
这种涉及到几个文件的项目通常可以锻炼到我们模块化编程的思维,啥是模块化编程呢?
模块化编程:模块化编程就是把我们的一整个项目,分成很多模块(比如我们生产汽车,可以分为生产发动机、生产轮胎、生产车架、组装等)而一个程序工程包含多个源文件(.c 文件和 .h 文件),每个 .c 文件可以被称为一个模块,每一个模块都有其各自的功能,而每一个.h文件则是声明该模块,相当于功能说明书 ,大大提高代码可阅读性,不会使代码显得臃肿
实现效果:
以下图为模板通讯录进行还原,其中包含序号、姓名、年龄、性别、联系电话、家庭住址,并且为其添加最基本的增删改查以及排序等功能。 🌞
首先开始执行程序,打印一张菜单
1.添加联系人
2.删除联系人
3.搜索联系人
4.修改联系人
5.显示通讯录
6.排序通讯录(这里选择用年龄排序)
7.清理通讯录
0.退出通讯录
程序主体
每次完成操作后重新回到主菜单页面,直到输入0退出,所以程序主体我们可以选择一个循环来进行,菜单选择采用switch case语句
进入程序,首先出现在我们眼前的是菜单,所有首先我们要打印一个菜单
第一个选项是增加联系人操作,联系人信息包括姓名、年龄、性别、联系电话、家庭住址,我们可以将这一组信息打包进一个结构体里。
第二个选项是删除联系人,输入联系人的姓名对其进行删除。
第三个选项是搜索联系人,输入联系人的姓名,输出此联系人的信息。
- 第四个选项是修改,输入要修改联系人的姓名,对其进行修改
- 第五个选项是显示联系人列表,将通讯录内所有信息打印
- 第六个选项是排序通讯录,作者选择用年龄进行排序。
- 第七个选项是清空通讯录,清空通讯录
- 最后一个选项是退出程序,这里选择用0的 这里选择用0的原因是可以用这个0来作为程序循环的结束条件,退出后将通讯录信息保存到文件内,使得下次打开程序里面的联系人都还在。(每次进入程序先加载文件信息)。
代码实现
1.打印菜单
void menu() { printf("********************************\n"); printf("***** 1.Add 2.Del *****\n"); printf("***** 3.Search 4.Modif *****\n"); printf("***** 5.Show 6.Sort *****\n"); printf("***** 7.clear 0.Exit *****\n"); printf("********************************\n"); }
菜单是程序不可或缺的一部分,让使用者知道怎么操作。
2.main函数及枚举类
enum option { Exit, Add, Del, Search, Modify, Show, Sort, Clear }; int main() { int input = 0; Contact con; //创建一个通讯录 InitContact(&con); do { menu(); printf("请选择要进行的操作\n"); scanf("%d", &input); switch (input) { case Add: AddContact(&con); //添加联系人 break; case Del: DelContact(&con); //删除联系人 break; case Search: SearchContact(&con); //搜索联系人 break; case Modify: ModifyContact(&con); //修改联系人 break; case Show: ShowContact(&con); //展示通讯录 break; case Sort: SortContact(&con); //排序通讯录 break; case Clear: ClearContact(&con); //清理通讯录 break; case Exit: SaveContact(&con); //保存通讯录 ExitContact(&con); //退出通讯录 printf("退出成功\n"); break; default: printf("输入错误,请重新输入\n"); break; } } while (input); }
进入程序,直接一张菜单弹出所以这里选择do。while语句,再用switch语句进行判断,当用户选择对应的选项执行对应的函数。
一个联系人的信息包括姓名、年龄、性别、联系电话、家庭住址,最适合装载此信息的变量肯定就是结构体了。
🌈这里使用一些宏定义的原因是方便修改,如果以后通讯录想修改某字符串的或某重要变量的长度可以很快速的找到,并且只修改这一个地方即可。
- 设计好用于装载联系人的结构体
#define MAX_NAME 20 //姓名长度 #define MAX_SEX 5 //性别长度 #define MAX_NUMBER 15 //电话号码长度 #define MAX_ADDRESS 20 //家庭住址长度 #define DEFAULT_SZ 3 //动态内存管理用到的默认通讯录的联系人个数 //通讯录数量为什么不设置成固定的值❔一是节省空间,二是固定的如果装满了需要改写太麻烦。 #define DEFAULT_INC 2 //动态内存管理用到变量,通讯录每次装满之后自增的个数。 typedef struct PeoInfo //联系人 { char name[MAX_NAME]; //姓名 int age; //年龄 char sex[MAX_SEX]; //性别 char number[MAX_NUMBER]; //电话号码 char address[MAX_ADDRESS]; //地址 }PeoInfo;
- 以上是联系人,整个通讯录需要很多个联系人,这里再使用一个结构体
typedef struct Contact { PeoInfo* data; //用动态内存方式存储信息,所有定义成指针而不是数组,指向联系人。 int sz; //当前联系人个数 int Capable; //当前能装载的最大联系人个数 }Contact; //静态版本 //typedef struct Contact //{ // PeoInfo data[100]; // int sz; //}Contact;
3.初始化通讯录
- 🌞定义好结构体之后要对结构体进行初始化,将当前联系人个数初始化为0,当前最大联系人个数初始化为DEFAULT_SZ
//初始化通讯录 void InitContact(Contact* c) { PeoInfo* ptr = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ); if (ptr == NULL) { printf("%s", strerror(errno)); return; } c->sz = 0; //将当前联系人个数初始化为0 c->Capable = DEFAULT_SZ; //当前最大联系人个数初始化为DEFAULT_SZ c->data = ptr; LoadingContact(c); //加载保存到文件夹的联系人信息 } //静态版本 //void InitContact(Contact *c) //{ // c->sz = 0; // memset(c->data, 0, sizeof(c->data)); //}
4.打印通讯录
- 先实现这个函数的原因是方便查找bug,打印整个通讯录
void CheckContact(Contact* c) { if (c->sz == c->Capable) //如果当前联系人数量等于当前联系人最大数量,最大数量+DEFAULT_INC { PeoInfo* ptr = (PeoInfo*)realloc(c->data, (DEFAULT_INC + c->Capable) * sizeof(PeoInfo)); if (ptr == NULL) //如果没申请到空间,报错返回 { printf("%s", strerror(errno)); return; } c->Capable = c->Capable + DEFAULT_INC; //把当前联系人最大数量+DEFAULT_INC c->data = ptr; printf("扩容成功\n"); } } void AddContact(Contact* c) { CheckContact(c); //检查当前空间是否足够 printf("请输入要添加的联系人\n"); printf("请输入姓名\n"); scanf("%s", c->data[c->sz].name); printf("请输入年龄\n"); scanf("%d", &(c->data[c->sz].age)); printf("请输入性别\n"); scanf("%s", c->data[c->sz].sex); printf("请输入号码\n"); scanf("%s", c->data[c->sz].number); printf("请输入地址\n"); scanf("%s", c->data[c->sz].address); c->sz++; } //静态版 //void AddContact(Contact* c) //{ // if (c->sz== 100) // { // printf("人数已满,不能继续添加\n"); // return; // } // printf("请输入要添加的联系人\n"); // printf("请输入姓名\n"); // scanf("%s", c->data[c->sz].name); // printf("请输入年龄\n"); // scanf("%d", &(c->data[c->sz].age)); // printf("请输入性别\n"); // scanf("%s", c->data[c->sz].sex); // printf("请输入号码\n"); // scanf("%s", c->data[c->sz].number); // printf("请输入地址\n"); // scanf("%s", c->data[c->sz].address); // c->sz++; //}
5.添加联系人
- 🌞动态版:如果当前联系人数量等于当前联系人最大数量,最大数量+DEFAULT_INC
void CheckContact(Contact* c) { if (c->sz == c->Capable) //如果当前联系人数量等于当前联系人最大数量,最大数量+DEFAULT_INC { PeoInfo* ptr = (PeoInfo*)realloc(c->data, (DEFAULT_INC + c->Capable) * sizeof(PeoInfo)); if (ptr == NULL) //如果没申请到空间,报错返回 { printf("%s", strerror(errno)); return; } c->Capable = c->Capable + DEFAULT_INC; //把当前联系人最大数量+DEFAULT_INC c->data = ptr; printf("扩容成功\n"); } } void AddContact(Contact* c) { CheckContact(c); //检查当前空间是否足够 printf("请输入要添加的联系人\n"); printf("请输入姓名\n"); scanf("%s", c->data[c->sz].name); printf("请输入年龄\n"); scanf("%d", &(c->data[c->sz].age)); printf("请输入性别\n"); scanf("%s", c->data[c->sz].sex); printf("请输入号码\n"); scanf("%s", c->data[c->sz].number); printf("请输入地址\n"); scanf("%s", c->data[c->sz].address); c->sz++; } //静态版 //void AddContact(Contact* c) //{ // if (c->sz== 100) // { // printf("人数已满,不能继续添加\n"); // return; // } // printf("请输入要添加的联系人\n"); // printf("请输入姓名\n"); // scanf("%s", c->data[c->sz].name); // printf("请输入年龄\n"); // scanf("%d", &(c->data[c->sz].age)); // printf("请输入性别\n"); // scanf("%s", c->data[c->sz].sex); // printf("请输入号码\n"); // scanf("%s", c->data[c->sz].number); // printf("请输入地址\n"); // scanf("%s", c->data[c->sz].address); // c->sz++; //}
6.删除联系人
- 🌞输入要删除的联系人的名称
void DelContact(Contact* c) { if (c->sz == 0) { printf("通讯录已空\n"); return; } char name[MAX_NAME]; printf("请输入要删除的人的姓名:\n"); scanf("%s", name); int pos = FindContact(c,name); if (pos == -1) { printf("查无此人\n"); } else { for (int i = pos; i < c->sz - 1; i++) { c->data[i] = c->data[i + 1]; } printf("删除成功\n"); c->sz--; } }
7.查找联系人
- 🧐输入联系人姓名,查找对应联系人
int FindContact(Contact* c,char* name)//找到返回0,没找到返回-1 { int pos = 0; for (int i = 0; i < c->sz; i++) { pos = strcmp(name, c->data[i].name); if (pos == 0) { return pos; } } return -1; } void SearchContact(Contact* c) { if (c->sz == 0) { printf("通讯录已空\n"); return; } char name[MAX_NAME]; printf("请输入要删除的人的姓名:\n"); scanf("%s", name); int pos = FindContact(c, name); if (pos == -1) { printf("查无此人\n"); } else { printf("%20s %5s %5s %15s %20s\n", "姓名", "年龄", "性别", "号码", "地址\n"); printf("%20s %5d %5s %15s %20s\n", c->data[pos].name, c->data[pos].age, c->data[pos].sex, c->data[pos].number, c->data[pos].address); } }
8.修改联系人信息
- 🌞输入联系人名称,修改其对应信息
int FindContact(Contact* c,char* name) //找到返回0,没找到返回-1 { int pos = 0; for (int i = 0; i < c->sz; i++) { pos = strcmp(name, c->data[i].name); if (pos == 0) { return pos; } } return -1; } void ModifyContact(Contact* c) { if (c->sz == 0) { printf("通讯录已空\n"); return; } char name[MAX_NAME]; printf("请输入要修改的人的姓名:\n"); scanf("%s", name); int pos = FindContact(c, name); if (pos == -1) { printf("查无此人\n"); } else { printf("请输入姓名\n"); scanf("%s", c->data[pos].name); printf("请输入年龄\n"); scanf("%d", &(c->data[pos].age)); printf("请输入性别\n"); scanf("%s", c->data[pos].sex); printf("请输入号码\n"); scanf("%s", c->data[pos].number); printf("请输入地址\n"); scanf("%s", c->data[pos].address); printf("修改成功!!\n"); } }
9.排序联系人(使用年龄排序)
- 🌞用库函数qsort实现即可,qsort函数的用法
int CmpPeoAge(const void* a, const void* b) { return ((PeoInfo*)a)->age - ((PeoInfo*)b)->age; } void SortContact(Contact* c) { qsort(c->data, c->sz, sizeof(PeoInfo), CmpPeoAge); printf("排序完成\n"); }
10.清理通讯录
- 🌞清理全部联系人信息
void ClearContact(Contact* c) { memset(c->data, 0, c->sz); //将所有空间置0 PeoInfo* ptr = (PeoInfo*)realloc(c->data, DEFAULT_SZ*sizeof(PeoInfo)); //重新申请一块初始大小的空间 if (ptr == NULL) { printf("清理失败\n"); return; } c->sz = 0; c->Capable = DEFAULT_SZ; c->data = ptr; printf("清理成功\n"); }
11.保存通讯录到文件
- 将所有联系人信息保存到文件夹
void SaveContact(Contact* c) { FILE* pf = fopen("ContactData.txt", "wb"); if (pf == NULL) { perror("fopen:"); return; } for (int i = 0; i < c->sz; i++) { fwrite(c->data+i, sizeof(PeoInfo), 1, pf); } fclose(pf); pf = NULL; printf("保存成功"); }
12.退出程序
void ExitContact(Contact* c) { free(c->data); c->data = NULL; c->Capable = 0; c->sz = 0; }
文件分类
🌞🌞为了使代码更有阅读性,我们不建议把所有函数写在一个文件里,所以这里分成三个文件,模块化管理
test.c
这个文件包含程序的主体构思,程序用到的一系列函数我们封装在其他文件夹,这里只需要引入头文件即可
#include "contact.h" void menu() { printf("********************************\n"); printf("***** 1.Add 2.Del *****\n"); printf("***** 3.Search 4.Modif *****\n"); printf("***** 5.Show 6.Sort *****\n"); printf("***** 7.clear 0.Exit *****\n"); printf("********************************\n"); } enum option { Exit, Add, Del, Search, Modify, Show, Sort, Clear }; int main() { int input = 0; Contact con; InitContact(&con); do { menu(); printf("请选择要进行的操作\n"); scanf("%d", &input); switch (input) { case Add: AddContact(&con); break; case Del: DelContact(&con); break; case Search: SearchContact(&con); break; case Modify: ModifyContact(&con); break; case Show: ShowContact(&con); break; case Sort: SortContact(&con); break; case Clear: ClearContact(&con); break; case Exit: SaveContact(&con); ExitContact(&con); printf("退出成功\n"); break; default: printf("输入错误,请重新输入\n"); break; } } while (input); }
contact.c
所有对通讯录进行操作的函数全部放在这里
#include "contact.h" //加载通讯录 void LoadingContact(Contact* c) { FILE* pf = fopen("ContactData.txt", "rb"); if (pf == NULL) { perror("loadingContact:fopen:"); return; } while (fread(c->data+c->sz, sizeof(PeoInfo), 1, pf)) { c->sz++; CheckContact(c); } fclose(pf); pf = NULL; } //初始化通讯录 void InitContact(Contact* c) { PeoInfo* ptr = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ); if (ptr == NULL) { printf("%s", strerror(errno)); return; } c->sz = 0; c->Capable = DEFAULT_SZ; c->data = ptr; LoadingContact(c); } //静态添加联系人 //void AddContact(Contact* c) //{ // if (c->sz == 100) // { // printf("人数已满,不能继续添加\n"); // return; // } // printf("请输入要添加的联系人\n"); // printf("请输入姓名\n"); // scanf("%s", c->data[c->sz].name); // printf("请输入年龄\n"); // scanf("%d", &(c->data[c->sz].age)); // printf("请输入性别\n"); // scanf("%s", c->data[c->sz].sex); // printf("请输入号码\n"); // scanf("%s", c->data[c->sz].number); // printf("请输入地址\n"); // scanf("%s", c->data[c->sz].address); // c->sz++; //} void CheckContact(Contact* c) { if (c->sz == c->Capable) { PeoInfo* ptr = (PeoInfo*)realloc(c->data, (DEFAULT_INC + c->Capable) * sizeof(PeoInfo)); if (ptr == NULL) { printf("%s", strerror(errno)); return; } c->Capable = c->Capable + DEFAULT_INC; c->data = ptr; printf("扩容成功\n"); } } //动态添加联系人 void AddContact(Contact* c) { CheckContact(c); printf("请输入要添加的联系人\n"); printf("请输入姓名\n"); scanf("%s", c->data[c->sz].name); printf("请输入年龄\n"); scanf("%d", &(c->data[c->sz].age)); printf("请输入性别\n"); scanf("%s", c->data[c->sz].sex); printf("请输入号码\n"); scanf("%s", c->data[c->sz].number); printf("请输入地址\n"); scanf("%s", c->data[c->sz].address); c->sz++; } int FindContact(Contact* c, char* name) { int pos = 0; for (int i = 0; i < c->sz; i++) { int pos1 = strcmp(name, c->data[i].name); if (pos1 == 0) { return pos; } pos++; } return -1; } void DelContact(Contact* c) { if (c->sz == 0) { printf("通讯录已空\n"); return; } char name[MAX_NAME]; printf("请输入要删除的人的姓名:\n"); scanf("%s", name); int pos = FindContact(c, name); if (pos == -1) { printf("查无此人\n"); } else { for (int i = pos; i < c->sz - 1; i++) { c->data[i] = c->data[i + 1]; } printf("删除成功\n"); c->sz--; } } void ShowContact(Contact* c) { printf("%2s %20s %5s %5s %15s %20s\n","序号", "姓名", "年龄", "性别", "号码", "地址\n"); for (int i = 0; i < c->sz; i++) { printf("%2d %20s %5d %5s %15s %20s\n", i+1,c->data[i].name, c->data[i].age, c->data[i].sex, c->data[i].number, c->data[i].address); } } void SearchContact(Contact* c) { if (c->sz == 0) { printf("通讯录已空\n"); return; } char name[MAX_NAME]; printf("请输入要删除的人的姓名:\n"); scanf("%s", name); int pos = FindContact(c, name); if (pos == -1) { printf("查无此人\n"); } else { printf("%20s %5s %5s %15s %20s\n", "姓名", "年龄", "性别", "号码", "地址\n"); printf("%20s %5d %5s %15s %20s\n", c->data[pos].name, c->data[pos].age, c->data[pos].sex, c->data[pos].number, c->data[pos].address); } } void ModifyContact(Contact* c) { if (c->sz == 0) { printf("通讯录已空\n"); return; } char name[MAX_NAME]; printf("请输入要修改的人的姓名:\n"); scanf("%s", name); int pos = FindContact(c, name); if (pos == -1) { printf("查无此人\n"); } else { printf("请输入姓名\n"); scanf("%s", c->data[pos].name); printf("请输入年龄\n"); scanf("%d", &(c->data[pos].age)); printf("请输入性别\n"); scanf("%s", c->data[pos].sex); printf("请输入号码\n"); scanf("%s", c->data[pos].number); printf("请输入地址\n"); scanf("%s", c->data[pos].address); printf("修改成功!!\n"); } } int CmpPeoAge(const void* a, const void* b) { return ((PeoInfo*)a)->age - ((PeoInfo*)b)->age; } void SortContact(Contact* c) { qsort(c->data, c->sz, sizeof(PeoInfo), CmpPeoAge); printf("排序完成\n"); } void ClearContact(Contact* c) { memset(c->data, 0, c->sz); PeoInfo* ptr = (PeoInfo*)realloc(c->data, DEFAULT_SZ*sizeof(PeoInfo)); if (ptr == NULL) { printf("清理失败\n"); return; } c->sz = 0; c->Capable = DEFAULT_SZ; c->data = ptr; printf("清理成功\n"); } void ExitContact(Contact* c) { free(c->data); c->data = NULL; c->Capable = 0; c->sz = 0; } void SaveContact(Contact* c) { FILE* pf = fopen("ContactData.txt", "wb"); if (pf == NULL) { perror("fopen:"); return; } for (int i = 0; i < c->sz; i++) { fwrite(c->data+i, sizeof(PeoInfo), 1, pf); } fclose(pf); pf = NULL; printf("保存成功"); }
contact.h
将主程序所需要的函数全部在头文件中声明,增加代码阅读性
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_NUMBER 15 #define MAX_ADDRESS 20 typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char number[MAX_NUMBER]; char address[MAX_ADDRESS]; }PeoInfo; typedef struct Contact { PeoInfo data[100]; int sz; }Contact; void InitContact(Contact *c); void AddContact(Contact *c); void DelContact(Contact *c); void ShowContact(Contact *c); void SearchContact(Contact *c); void ModifyContact(Contact *c); void SortContact(Contact* c);
撒花
这就是实现通讯录的全部内容了,创作不易,还请各位小伙伴多多点赞👍关注收藏⭐,以后也会更新各种小游戏还有关于c语言的博客,撒花!