通讯录结构
- 参考代码:
//通讯录结构 struct Contact { //联系人总容量 int capacity; //标记动态通讯录的首地址 struct Peoinfo *data; //使用个数记录 int sz; };
初始化通讯录
使用malloc来开辟一个默认大小的动态空间
- 参考代码:
//初始化通讯录 void InitContact(struct Contact* p) { p->capacity = C_SZ; p->sz = 0; p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ); LoadContact(p); }
//默认容量大小 #define C_SZ 2 //默认添加大小 #define PLUS 3
通讯录增容
- 注意点:
- 添加通讯录联系人时进行判断(这里我们封装成一个函数)
- 当人数等于容量大小就增容(用realloc来调整动态开辟空间的大小)
- 增容失败则打印错误并结束程序
- 参考代码:
//检查通讯录是否满 void CheakContact(struct Contact* p) { if (p->sz == p->capacity) { //如果使用人数已经满了,则申请开辟更大的空间 struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS)); //ptr不为NULL则开辟成功 if (ptr != NULL) { p->data = ptr; p->capacity += PLUS; } else { perror("CheakContact");//打印出错误原因 exit(1);//结束程序 } } }
通讯录销毁
- 注意点:
退出通讯录时我们需要将动态开辟的空间给释放(使用free函数)(否则可能造成内存泄漏)
- 参考代码:
//动态通讯录申请空间后不用还得释放 void DestoryContact(struct Contact* p) { free(p->data); p -> data = NULL; p->capacity = 0; p->sz = 0; }
数据保存
在我们的通讯录写好后,我们如果还想让通讯录能够保存我们所输入联系人数据(在退出后再打开通讯录)
注:这就需要一点文件操作的知识了(在后面的专题中会仔细讲解)
输出数据
在通讯录运行结束前,我们让通讯录联系人的数据以二进制的形式输出到指定的文件中
(即将数据保存在文件中)
参考代码:
//保存通讯录数据 void SaveContact(struct Contact* p) { //fopen函数:找到指定文件并返回文件的地址 //wb:以只写的方式打开(为了输出数据,打开一个二进制文件) FILE* pf = fopen("contact.txt", "wb"); if (pf == NULL) { //打开失败则打印错误信息 perror("SaveContact"); return; } for (int i = 0; i < p->sz; i++) { //fwrite:二进制输出 fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf); } //用完pf后进行释放 fclose(pf); pf = NULL; return; }
输入数据(加载数据)
当下一次进入通讯录将原来保存的数据输入到内存中
- 参考代码:
//加载通讯录数据 void LoadContact(struct Contact*p) { //rb:以只读的方式打开一个二进制文件 FILE* pf = fopen("contact.txt","rb"); if (pf == NULL) { perror("Loadcontact"); return; } //暂时保存数据 struct Peoinfo tmp = { 0 }; //fread:二进制输入(输入的数据完整则返回1,否则返回0) while (fread(&tmp, sizeof(struct Peoinfo), 1, pf)) { //输入时判断是否需要增容 CheakContact(p); p->data[p->sz] = tmp; p->sz++; } //释放 fclose(pf); pf = NULL; return; }
附上源码
- contact.h
#define _CRT_SECURE_NO_WARNINGS #pragma once //特定数据宏定义方式,包含各种头文件,以及结构体与函数的声明 #include<stdio.h> #include<string.h> #include<stdlib.h> //信息接收最大数目(预处理指令便于维护) #define NAME_MAX 20 #define NUMBER_MAX 11 #define SEX_MAX 10 #define ADDR_MAX 30 //默认容量大小 #define C_SZ 2 //默认添加大小 #define PLUS 3 //个人信息结构 struct Peoinfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; int number[NUMBER_MAX]; char addr[ADDR_MAX]; }; //通讯录结构 struct Contact { //联系人总容量 int capacity; //标记动态通讯录的首地址 struct Peoinfo *data; //使用个数记录 int sz; }; //初始化通讯录(清空通讯录) void InitContact(struct Contact* p); //添加通讯录联系人 void AddContact(struct Contact* p); //删除通讯录联系人 void DelContact(struct Contact* p); //查找通讯录联系人 void SearchContact(struct Contact* p); //修改通讯录联系人信息 void ModifyContact(struct Contact* p); //展示通讯录联系人 void ShowContact(struct Contact* p); //通讯录排序 void SortContact(struct Contact* p); //清除通讯录(释放) void DestoryContact(struct Contact* p); void SaveContact(struct Contact* p); void LoadContact(struct Contact* p); //操作选项 void option(); int cmp_con_name(void* e1, void* e2); int cmp_con_age(void* e1, const void* e2); int cmp_con_sex(const void* e1, const void* e2); int cmp_con_number(const void* e1, const void* e2); int cmp_con_addr(const void* e1, const void* e2);
- contact.c
//实现通讯录功能函数 #include "contact.h" //检查通讯录是否满 void CheakContact(struct Contact* p) { if (p->sz == p->capacity) { //如果使用人数已经满了,则申请开辟更大的空间 struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS)); //ptr不为NULL则开辟成功 if (ptr != NULL) { p->data = ptr; p->capacity += PLUS; } else { perror("LoadContact"); exit(1); } } } //加载通讯录数据 void LoadContact(struct Contact*p) { //rb:以只读的方式打开一个二进制文件 FILE* pf = fopen("contact.txt","rb"); if (pf == NULL) { perror("Loadcontact"); return; } //暂时保存数据 struct Peoinfo tmp = { 0 }; //fread:二进制输入(输入的数据完整则返回1,否则返回0) while (fread(&tmp, sizeof(struct Peoinfo), 1, pf)) { //输入时判断是否需要增容 CheakContact(p); p->data[p->sz] = tmp; p->sz++; } //释放 fclose(pf); pf = NULL; return; } //初始化通讯录 void InitContact(struct Contact* p) { p->capacity = C_SZ; p->sz = 0; p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ); LoadContact(p); } //添加通讯录联系人 void AddContact(struct Contact* p) { CheakContact(p); printf("请输入新联系人的名字:"); scanf("%s", p->data[p->sz].name); printf("请输入新联系人的年龄:"); scanf("%d", &p->data[p->sz].age); printf("请输入新联系人的性别:"); scanf("%s", p->data[p->sz].sex); printf("请输入新联系人的电话号码:"); scanf("%s", p->data[p->sz].number); printf("请输入新联系人的地址:"); scanf("%s", p->data[p->sz].addr); printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name); p->sz++; return; } //删除通讯录联系人 void DelContact(struct Contact* p) { if (p->sz == 0) { printf("没有联系人,无法删除!\n"); return; } char name[NAME_MAX]; printf("请输入要删除的联系人名字:\n"); scanf("%s", &name); for (int i = 0; i < p->sz; i++) { //字符串比较采用strcmp if (strcmp(name, p->data[i].name) == 0) { for (int j = i; j < p->sz - 1; j++) { //字符串赋值采用strcpy strcpy(p->data[j].name, p->data[j + 1].name); p->data[j].age = p->data[j + 1].age; strcpy(p->data[j].sex, p->data[j + 1].sex); strcpy(p->data[j].number, p->data[j + 1].number); strcpy(p->data[j].addr, p->data[j + 1].addr); } p->sz--; printf("删除通讯录联系人%s成功!\n", name); return; } } printf("在通讯录中无法查找到联系人%s!\n", name); return; } //查找通讯录联系人 void SearchContact(struct Contact* p) { char name[NAME_MAX]; printf("请输入要进行操作的联系人名字:\n"); scanf("%s", &name); for (int i = 0; i < p->sz; i++) { if (strcmp(name, p->data[i].name) == 0) { printf("联系人的名字:%s\n", p->data[i].name); printf("联系人的年龄:%d\n", p->data[i].age); printf("联系人的性别:%s\n", p->data[i].sex); printf("联系人的电话号码:%s\n", p->data[i].number); printf("联系人的地址:%s\n", p->data[i].addr); return; } } printf("在通讯录中无法查找到该联系人!\n"); return; } //操作选项 void option() { printf("**************************\n"); printf("****1.name 2.age****\n"); printf("****3.sex 4.number****\n"); printf("****5.addr 0.exit****\n"); printf("**************************\n"); } //修改通讯录联系人信息 void ModifyContact(struct Contact* p) { int pos = -1; int intput = -1; ShowContact(p); char name[NAME_MAX]; printf("请输入要进行操作的联系人名字:\n"); scanf("%s", &name); for (int i = 0; i < p->sz; i++) { if (strcmp(name, p->data[i].name) == 0) { //找到则记录位置并进行展示 printf("联系人的名字:%s\n", p->data[i].name); printf("联系人的年龄:%d\n", p->data[i].age); printf("联系人的性别:%s\n", p->data[i].sex); printf("联系人的电话号码:%s\n", p->data[i].number); printf("联系人的地址:%s\n", p->data[i].addr); pos = i; break; } } //找不到则退出 if (pos == -1) { printf("在通讯录中无法查找到该联系人!\n"); return; } do { option(); printf("请输入要修改的选项:->\n"); scanf("%d", &intput); switch (intput) { case 0: printf("退出修改成功!\n"); break; case 1: printf("请输入新的名字:\n"); scanf("%s", &p->data[pos].name); break; case 2: printf("请输入新的年龄:\n"); scanf("%d", &p->data[pos].age); break; case 3: printf("请输入新的性别:\n"); scanf("%d", &p->data[pos].sex); break; case 4: printf("请输入新的号码:\n"); scanf("%d", &p->data[pos].number); break; case 5: printf("请输入新的地址:\n"); scanf("%d", &p->data[pos].addr); break; default: printf("输入错误,请重新输入:\n"); } } while (intput); return; } //展示通讯录联系人 void ShowContact(struct Contact* p) { //制表 printf("————————————————————————————————————————————————\n"); printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n", "name", "age", "sex", "tele", "addr"); printf("————————————————————————————————————————————————\n"); for (int i = 0; i < p->sz; i++) { printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n", p->data[i].name, p->data[i].age, p->data[i].sex, p->data[i].number, p->data[i].addr); printf("————————————————————————————————————————————————\n"); } return; } //qsort函数参数比较函数的实现 int cmp_con_name(const void* e1, const void* e2) { return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name); } int cmp_con_age(const void* e1, const void* e2) { return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age; } int cmp_con_sex(const void* e1, const void* e2) { return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex); } int cmp_con_number(const void* e1, const void* e2) { return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number); } int cmp_con_addr(const void* e1, const void* e2) { return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr); } //通讯录排序 void SortContact(struct Contact* p) { int intput = -1; do { option(); printf("请输入排序方式的选项:\n"); scanf("%d", &intput); switch (intput) { case 0: printf("退出修改成功!\n"); break; case 1: qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name); ShowContact(p); break; case 2: qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age); ShowContact(p); break; case 3: qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex); ShowContact(p); break; case 4: qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number); ShowContact(p); break; case 5: qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr); ShowContact(p); break; default: printf("输入错误,请重新输入:\n"); } } while (intput); return; } //动态通讯录申请空间后不用还得释放 void DestoryContact(struct Contact* p) { free(p->data); p -> data = NULL; p->capacity = 0; p->sz = 0; } //保存通讯录数据 void SaveContact(struct Contact* p) { //fopen函数:找到指定文件并返回文件的地址 //wb:以只写的方式打开(为了输出数据,打开一个二进制文件) FILE* pf = fopen("contact.txt", "wb"); if (pf == NULL) { //打开失败则打印错误信息 perror("SaveContact"); return; } for (int i = 0; i < p->sz; i++) { //fwrite:二进制输出 fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf); } //用完pf后进行释放 fclose(pf); pf = NULL; return; }
- test.c
//写通讯录的整体流程和逻辑 #include "contact.h" void menu() { printf("**************************\n"); printf("****1.add 2.del****\n"); printf("****3.search 4.modify****\n"); printf("****5.show 6.sort****\n"); printf("****7.clear 0.exit****\n"); printf("**************************\n"); } //回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录) void Calo(void(*pc)(struct Peoinfo* i), struct Contact* p) { pc(p);//pc()为函数调用,参数传入p即通讯录地址 } //主体逻辑 int main() { int intput; struct Contact con; InitContact(&con); //写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参) void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,DestoryContact }; //基本逻辑循环 do { menu(); printf("请输入接下来想要进行的操作:->\n"); scanf("%d", &intput); if (intput > 0 && intput <= 7) { printf("成功进入所选择项操作!\n"); Calo(table[intput], &con);//回调操作 } else if (intput == 0) { SaveContact(&con); Calo(table[7], &con); printf("退出操作成功!!\n"); break; } else { printf("输入项错误!请重新选择:->\n"); } } while (intput);//intput为0则退出 }