在C语言学习结束之际,谨以此篇文章来对C语言的学习告一段落。
纲要:
通讯录的静态版本 通讯录的动态版本 通讯录的带文件版本因为三种实现方法除了储存形式不同,其他都基本相同,所以我们重点论述静态版本的实现,以及它们不同的储存方式。
一.通讯录的静态版本
为什么叫它为静态版本呢,因为在此部分的储存是以数组来储存的,那对于各种各样的信息,我们要拿什么数组来存放它呢?当然是结构体数组了,所以我们来定义一个结构体来表示个人信息:
//采用宏的目的是方便日后修改 #define NAME_MAX 20 #define SEX_MAX 5 #define PNUM_MAX 13 #define ADDR_MAX 20 #define MAX 10 //存放个人信息的结构体 typedef struct Data { char name[NAME_MAX];//姓名 int age;//年龄 char sex[SEX_MAX];//性别 char pnum[PNUM_MAX];//电话 char addr[ADDR_MAX];//地址 } Data;
现在有了个人信息的结构体,我们需要再来一个结构体来存放它的数组及数组内有效信息的个数,即:
//存放MAX个个人信息的通讯录 typedef struct Contact { Data data[MAX]; int size; } Contact;
那么,准备工作做好之后,我们就开始正式实现了,首先我们肯定是要先创建一个通讯录,这时我们再来想一想,我们就这样创建之后,我们是否可以直接使用呢?
对此我们来看一张图片:
我们发现,现在它里面都放着一些随机值,所以我们需要将其初始化一下,来方便我们的使用:
void ContactInit(Contact *p) { //保证p不为NULL assert(p); //置零 memset(p->data, 0, sizeof(p->data)); p->size = 0; }
我们再来看一下结果:
我们发现,现在它内部已经被我们置为了0;接着我们做的就是添加联系人了,不过在此之前,我们不妨先做一个菜单来显示我们都有一些什么功能:
void menu() { //打印菜单 printf("******************************************\n"); printf("****** 1.add 2.del ******\n"); printf("****** 3.search 4.modify ******\n"); printf("****** 5.show 6.sort ******\n"); printf("****** 7.help 0.exit ******\n"); printf("******************************************\n"); }
接着是我们的帮助选项:
//打印帮助信息 void ContactHelp(Contact *p) { printf("*******************************************\n"); printf("****** add ---- 添加联系人信息 ******\n"); printf("****** del ---- 删除联系人信息 ******\n"); printf("****** search ---- 查找联系人信息 ******\n"); printf("****** modify ---- 修改联系人信息 ******\n"); printf("****** show ---- 展示联系人信息 ******\n"); printf("****** help ---- 帮助信息 ******\n"); printf("****** sort ---- 排序联系人信息 ******\n"); printf("****** exit ---- 退出通讯录 ******\n"); printf("*******************************************\n"); }
以及我们来用枚举来定义一些常量,方便在switch()结构中 来辨别它走了哪条路线:
//枚举来作为常量使得在看代码时比较清晰 enum choice { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, HELP };
以及写出我们的选择结构:我们采用do-while循环
void test() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); int input = 0; do { menu(); printf("请输入你的选择:> "); scanf("%d", &input); switch (input) { case ADD: ContactAdd(p); break; case DEL: ContactDel(p); break; case SEARCH: ContactSearch(p); break; case MODIFY: ContactModify(p); break; case SHOW: ContactShow(p); break; case SORT: ContactSort(p); break; case HELP: ContactHelp(p); break; case EXIT: ContactExit(p); break; default: printf("输入非法!"); } } while (input); }
这样的好处是当用户输入0时循环便自己停止,不用我们再次去判断当用户输入0时我们要退出的问题,接下来我们就来填写我们函数的内容了:
1.添加联系人
//添加联系人 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人容量已经等于最大容量了 if(p->size==MAX) { printf("通讯录已满,请删除一些后继续添加!\n"); return ; } Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; }
我们要是观察到我们输入的信息,最好就是把我们所输入的信息给打印出来:
2.展示联系人
//展示联系人信息 void ContactShow(Contact *p) { if (p->size == 0) { printf("通讯录中并无一人!\n"); return ; } int i = 0; printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); for (i = 0; i < p->size; i++) { printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[i].name, p->data[i].sex, p->data[i].age, p->data[i].pnum, p->data[i].addr); } }
测试结果:
接下来就是删除联系人了
3.删除联系人
首先删除联系人肯定需要查找信息,又因为后面的几个函数也要用到它,所以我们单独来写一个查找模块:
//查找模块 int ContactFind(Contact *p, char *FindData) { assert(p); int i = 0; for (i = 0; i < p->size; i++) { if (strcmp(p->data[i].name, FindData) == 0) { return i;//找到就返回下标 } } return -1;//找不到就返回-1 }
删除:
//删除联系人 void ContactDel(Contact *p) { assert(p); char DelName[NAME_MAX] = {0}; printf("请输入你要删除的联系人姓名:>"); scanf("%s", DelName); int ret = ContactFind(p, DelName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { int j = 0; for (j = ret; j < p->size; j++) { //从前往后依次挪动覆盖 p->data[j] = p->data[j + 1]; } //删除完成之后,联系人个数减一 p->size--; } }
4.查找联系人信息
//查找联系人 void ContactSearch(Contact *p) { assert(p); char SearchName[NAME_MAX]; printf("请输入你要查找的联系人姓名:>\n"); scanf("%s",SearchName); //查找有无此人 int ret = ContactFind(p,SearchName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { printf("你所查找的联系人信息为:\n"); printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[ret].name, p->data[ret].sex, p->data[ret].age, p->data[ret].pnum, p->data[ret].addr); } }
5.修改联系人信息
//修改联系人信息 void ContactModify(Contact *p) { assert(p); char ModifyName[NAME_MAX]; printf("请输入你要修改的联系人姓名:>"); scanf("%s",ModifyName); int ret = ContactFind(p,ModifyName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[ret] = person; } }
6.排序联系人 --- 我们使用 qsort 来排序
enum sort_by { NAME=1, SEX, AGE, PNUM, ADDR };
void sort_menu() { printf(" SORT_MENU \n"); printf("******************************\n"); printf("**** 1.name ****\n"); printf("**** 2.sex ****\n"); printf("**** 3.age ****\n"); printf("**** 4.pnum ****\n"); printf("**** 5.addr ****\n"); printf("******************************\n"); } int sort_by_name(const void *s1, const void *s2) { return strcmp(((Data *) s1)->name, ((Data *) s2)->name); } int sort_by_sex(const void *s1, const void *s2) { return strcmp(((Data *) s1)->sex, ((Data *) s2)->sex); } int sort_by_age(const void *s1, const void *s2) { return ((Data *) s1)->age - ((Data *) s2)->age; } int sort_by_pnum(const void *s1, const void *s2) { return strcmp(((Data *) s1)->pnum, ((Data *) s2)->pnum); } int sort_by_addr(const void *s1, const void *s2) { return strcmp(((Data *) s1)->addr, ((Data *) s2)->addr); } //排序联系人 void ContactSort(Contact *p) { assert(p); int choice; sort_menu(); printf("请选择排序的参考量:>"); scanf("%d", &choice); switch (choice) { case NAME: qsort(p->data, p->size, sizeof(Data), sort_by_name); break; case SEX: qsort(p->data, p->size, sizeof(Data), sort_by_sex); break; case AGE: qsort(p->data, p->size, sizeof(Data), sort_by_age); break; case PNUM: qsort(p->data, p->size, sizeof(Data), sort_by_pnum); break; case ADDR: qsort(p->data, p->size, sizeof(Data), sort_by_addr); break; default: printf("输入有误,请检查输入!\n"); } }
到这,我们的静态通讯录就完了,但是我们仍可对用户操作优化一下,如:及时的清屏等,以及暂停:
例:
do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); system("cls"); switch (input) { case QUIT: printf("退出通讯录!\n"); break; case ADD: add(p); system("pause"); system("cls"); break; case DEL: del(p); system("pause"); system("cls"); break; case SEARCH: search(p); system("pause"); system("cls"); break; case MODIFY: modify(p); system("pause"); system("cls"); break; case SHOW: show(p); system("pause"); system("cls"); break; case SORT: sort(p); system("pause"); system("cls"); break; case HELP: help(); system("pause"); system("cls"); break; default: printf("非法输入,请检查输入!\n"); system("pause"); system("cls"); break; } } while (input);
这样我们的界面看起来就干净多了,但是有没有发现,我们为了达到这个效果,我们写了很多重复的代码!
那这样我们应该怎么办呢 --- 还记得我们之前所提到的函数指针数组吗?
void test2() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); //用一个函数指针数组来存放函数指针 void (*fun[])(Contact *) ={ContactExit, ContactAdd, ContactDel, ContactSearch, ContactModify, ContactShow, ContactSort, ContactHelp}; int input = 0;//存放用户选择的信息 do{ menu(); printf("请输入你的选择:>"); scanf("%d", &input); system("cls"); if(input>=0&&input<=sizeof(fun)) { fun[input](p); system("cls"); } else { system("cls"); printf("输入非法,请检查输入!\n"); } }while(input); }
这样是不是代码就少了很多!
所以完整代码如下:
Contact.h
//确保文件只包含一次 #ifndef CONTACT_CONTACT_H #define CONTACT_CONTACT_H #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <assert.h> //采用宏的目的是方便日后修改 #define NAME_MAX 20 #define SEX_MAX 8 #define PNUM_MAX 13 #define ADDR_MAX 20 #define MAX 10 //存放个人信息的结构体 typedef struct Data { char name[NAME_MAX];//姓名 int age;//年龄 char sex[SEX_MAX];//性别 char pnum[PNUM_MAX];//电话 char addr[ADDR_MAX];//地址 } Data; //存放MAX个个人信息的通讯录 typedef struct Contact Data data[MAX]; int size; } Contact; //枚举来作为常量使得在看代码时比较清晰 enum choice EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, HELP }; enum sort_by NAME=1, SEX, AGE, PNUM, ADDR //初始化通讯录 void ContactInit(Contact *p); //添加联系人 void ContactAdd(Contact* p); //删除联系人 void ContactDel(Contact* p); //查找联系人 void ContactSearch(Contact* p); //修改联系人信息 void ContactModify(Contact* p); //展示联系人信息 void ContactShow(Contact* p); //排序联系人 void ContactSort(Contact* p); //打印帮助 void ContactHelp(Contact* p); //退出通讯录 void ContactExit(Contact* p); #endif //CONTACT_CONTACT_H
Contact.c
#include "Contact.h" // 强调!!! //调试请加setbuf(stdout,NULL)!!! //查找模块 int ContactFind(Contact *p, char *FindData) { assert(p); int i = 0; for (i = 0; i < p->size; i++) { if (strcmp(p->data[i].name, FindData) == 0) { return i;//找到就返回下标 } } return -1;//找不到就返回-1 } void ContactInit(Contact *p) { //保证p不为NULL assert(p); //置零 memset(p->data, 0, sizeof(p->data)); p->size = 0; } //添加联系人 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人容量已经等于最大容量了 if (p->size == MAX) { printf("通讯录已满,请删除一些后继续添加!\n"); return; } Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; } //删除联系人 void ContactDel(Contact *p) { assert(p); char DelName[NAME_MAX] = {0}; printf("请输入你要删除的联系人姓名:>"); scanf("%s", DelName); int ret = ContactFind(p, DelName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { int j = 0; for (j = ret; j < p->size; j++) { //从前往后依次挪动覆盖 p->data[j] = p->data[j + 1]; } //删除完成之后,联系人个数减一 p->size--; } } //查找联系人 void ContactSearch(Contact *p) { assert(p); char SearchName[NAME_MAX]; printf("请输入你要查找的联系人姓名:>"); scanf("%s", SearchName); //查找有无此人 int ret = ContactFind(p, SearchName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { printf("你所查找的联系人信息为:\n"); printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[ret].name, p->data[ret].sex, p->data[ret].age, p->data[ret].pnum, p->data[ret].addr); } } //修改联系人信息 void ContactModify(Contact *p) { assert(p); char ModifyName[NAME_MAX]; printf("请输入你要修改的联系人姓名:>"); scanf("%s", ModifyName); int ret = ContactFind(p, ModifyName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[ret] = person; } } //展示联系人信息 void ContactShow(Contact *p) { if (p->size == 0) { printf("通讯录中并无一人!\n"); return; } int i = 0; printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); for (i = 0; i < p->size; i++) { printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[i].name, p->data[i].sex, p->data[i].age, p->data[i].pnum, p->data[i].addr); } } void sort_menu() { printf(" SORT_MENU \n"); printf("******************************\n"); printf("**** 1.name ****\n"); printf("**** 2.sex ****\n"); printf("**** 3.age ****\n"); printf("**** 4.pnum ****\n"); printf("**** 5.addr ****\n"); printf("******************************\n"); } int sort_by_name(const void *s1, const void *s2) { return strcmp(((Data *) s1)->name, ((Data *) s2)->name); } int sort_by_sex(const void *s1, const void *s2) { return strcmp(((Data *) s1)->sex, ((Data *) s2)->sex); } int sort_by_age(const void *s1, const void *s2) { return ((Data *) s1)->age - ((Data *) s2)->age; } int sort_by_pnum(const void *s1, const void *s2) { return strcmp(((Data *) s1)->pnum, ((Data *) s2)->pnum); } int sort_by_addr(const void *s1, const void *s2) { return strcmp(((Data *) s1)->addr, ((Data *) s2)->addr); } //排序联系人 void ContactSort(Contact *p) { assert(p); int choice; sort_menu(); printf("请选择排序的参考量:>"); scanf("%d", &choice); switch (choice) { case NAME: qsort(p->data, p->size, sizeof(Data), sort_by_name); break; case SEX: qsort(p->data, p->size, sizeof(Data), sort_by_sex); break; case AGE: qsort(p->data, p->size, sizeof(Data), sort_by_age); break; case PNUM: qsort(p->data, p->size, sizeof(Data), sort_by_pnum); break; case ADDR: qsort(p->data, p->size, sizeof(Data), sort_by_addr); break; default: printf("输入有误,请检查输入!\n"); } } //打印帮助信息 void ContactHelp(Contact *p) { printf("*******************************************\n"); printf("****** add ---- 添加联系人信息 ******\n"); printf("****** del ---- 删除联系人信息 ******\n"); printf("****** search ---- 查找联系人信息 ******\n"); printf("****** modify ---- 修改联系人信息 ******\n"); printf("****** show ---- 展示联系人信息 ******\n"); printf("****** help ---- 帮助信息 ******\n"); printf("****** sort ---- 排序联系人信息 ******\n"); printf("****** exit ---- 退出通讯录 ******\n"); printf("*******************************************\n"); } //退出通讯录 void ContactExit(Contact *p) { printf("exit !\n"); }
main.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.help 0.exit ******\n"); printf("******************************************\n"); } void test() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); int input = 0;//存放用户选择的信息 do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); switch (input) { case ADD: ContactAdd(p); break; case DEL: ContactDel(p); break; case SEARCH: ContactSearch(p); break; case MODIFY: ContactModify(p); break; case SHOW: ContactShow(p); break; case SORT: ContactSort(p); break; case HELP: ContactHelp(p); break; case EXIT: ContactExit(p); break; default: printf("输入非法!\n"); } } while (input); } void test2() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); //用一个函数指针数组来存放函数指针 void (*fun[])(Contact *) ={ContactExit, ContactAdd, ContactDel, ContactSearch, ContactModify, ContactShow, ContactSort, ContactHelp}; int input = 0;//存放用户选择的信息 do{ menu(); printf("请输入你的选择:>"); scanf("%d", &input); system("cls"); if(input>=0&&input<=sizeof(fun)) { fun[input](p); system("cls"); } else { system("cls"); printf("输入非法,请检查输入!\n"); } }while(input); } int main() { //test(); test2(); return 0; }
二.动态通讯录
动态实现的问题主要在于它的容量不再是一变不变的,而是可随着我们的数据量来变化的,所以在我们原来定义的Contact结构体就要微微改变一下了:
typedef struct Contact { Data* data;//存放数据 int size;//有效数据的个数 int capacity;//容量的大小 } Contact;
初始化函数也要改改:
//初始化 --- 动态 void ContactInit(Contact *p) { assert(p); p->data=NULL; p->size=0; p->capacity=1; }
那么这样,添加函数也有一定的变化:
//检查容量函数 void CheckCapacity(Contact *p) { assert(p); //如果联系人个数为0或与容量相同,就需要扩容 if (p->size == 0 || p->size == p->capacity) { //动态内存开辟 Data *ptr = (Data *) realloc(p->data, sizeof(Data) * p->capacity * 2); if (ptr == NULL)//开辟失败 { //报错 perror("CHECK CAPACITY ERROE !\n"); exit(-1); } //开辟成功,重新赋值 p->data = ptr; //扩容之后,容量也相应扩大 p->capacity *= 2; } //反之什么都不需要干 } //添加联系人 --- 动态 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人个数等于容量,或联系人个数等于0,这时我们就需要扩容了,我们来使用一个函数来干这事 CheckCapacity(p); Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; }
最后我们还要记得释放我们开辟的内存 --- 退出
//退出通讯录 --- 动态 void ContactExit(Contact *p) { //释放我们开辟的内存 free(p->data); printf("exit !\n"); }
动态通讯录的修改就只有这些:
完整代码展示:
Contact.h
//确保文件只包含一次 #ifndef CONTACT_CONTACT_H #define CONTACT_CONTACT_H #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <assert.h> //采用宏的目的是方便日后修改 #define NAME_MAX 20 #define SEX_MAX 8 #define PNUM_MAX 13 #define ADDR_MAX 20 #define MAX 10 //存放个人信息的结构体 typedef struct Data { char name[NAME_MAX];//姓名 int age;//年龄 char sex[SEX_MAX];//性别 char pnum[PNUM_MAX];//电话 char addr[ADDR_MAX];//地址 } Data; /* //存放MAX个个人信息的通讯录 --- 静态 typedef struct Contact { Data data[MAX]; int size; } Contact; */ //存放MAX个个人信息的通讯录 --- 动态 typedef struct Contact { Data* data;//存放数据 int size;//有效数据的个数 int capacity;//容量的大小 } Contact; //枚举来作为常量使得在看代码时比较清晰 enum choice { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, HELP }; enum sort_by { NAME=1, SEX, AGE, PNUM, ADDR }; //初始化通讯录 void ContactInit(Contact *p); //添加联系人 void ContactAdd(Contact* p); //删除联系人 void ContactDel(Contact* p); //查找联系人 void ContactSearch(Contact* p); //修改联系人信息 void ContactModify(Contact* p); //展示联系人信息 void ContactShow(Contact* p); //排序联系人 void ContactSort(Contact* p); //打印帮助 void ContactHelp(Contact* p); //退出通讯录 void ContactExit(Contact* p); #endif //CONTACT_CONTACT_H
Contact.c
#include "Contact.h" // 强调!!! //调试请加setbuf(stdout,NULL)!!! //查找模块 int ContactFind(Contact *p, char *FindData) { assert(p); int i = 0; for (i = 0; i < p->size; i++) { if (strcmp(p->data[i].name, FindData) == 0) { return i;//找到就返回下标 } } return -1;//找不到就返回-1 } /* //初始化 --- 静态 void ContactInit(Contact *p) { //保证p不为NULL assert(p); //置零 memset(p->data, 0, sizeof(p->data)); p->size = 0; } */ //初始化 --- 动态 void ContactInit(Contact *p) { assert(p); p->data = NULL; p->size = 0; p->capacity = 1; } /*//添加联系人 --- 静态 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人容量已经等于最大容量了 if (p->size == MAX) { printf("通讯录已满,请删除一些后继续添加!\n"); return; } Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; }*/ //检查容量函数 void CheckCapacity(Contact *p) { assert(p); //如果联系人个数为0或与容量相同,就需要扩容 if (p->size == 0 || p->size == p->capacity) { //动态内存开辟 Data *ptr = (Data *) realloc(p->data, sizeof(Data) * p->capacity * 2); if (ptr == NULL)//开辟失败 { //报错 perror("CHECK CAPACITY ERROE !\n"); exit(-1); } //开辟成功,重新赋值 p->data = ptr; //扩容之后,容量也相应扩大 p->capacity *= 2; } //反之什么都不需要干 } //添加联系人 --- 动态 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人个数等于容量,或联系人个数等于0,这时我们就需要扩容了,我们来使用一个函数来干这事 CheckCapacity(p); Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; } //删除联系人 void ContactDel(Contact *p) { assert(p); char DelName[NAME_MAX] = {0}; printf("请输入你要删除的联系人姓名:>"); scanf("%s", DelName); int ret = ContactFind(p, DelName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { int j = 0; for (j = ret; j < p->size; j++) { //从前往后依次挪动覆盖 p->data[j] = p->data[j + 1]; } //删除完成之后,联系人个数减一 p->size--; } } //查找联系人 void ContactSearch(Contact *p) { assert(p); char SearchName[NAME_MAX]; printf("请输入你要查找的联系人姓名:>"); scanf("%s", SearchName); //查找有无此人 int ret = ContactFind(p, SearchName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { printf("你所查找的联系人信息为:\n"); printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[ret].name, p->data[ret].sex, p->data[ret].age, p->data[ret].pnum, p->data[ret].addr); } } //修改联系人信息 void ContactModify(Contact *p) { assert(p); char ModifyName[NAME_MAX]; printf("请输入你要修改的联系人姓名:>"); scanf("%s", ModifyName); int ret = ContactFind(p, ModifyName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[ret] = person; } } //展示联系人信息 void ContactShow(Contact *p) { if (p->size == 0) { printf("通讯录中并无一人!\n"); return; } int i = 0; printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); for (i = 0; i < p->size; i++) { printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[i].name, p->data[i].sex, p->data[i].age, p->data[i].pnum, p->data[i].addr); } } void sort_menu() { printf(" SORT_MENU \n"); printf("******************************\n"); printf("**** 1.name ****\n"); printf("**** 2.sex ****\n"); printf("**** 3.age ****\n"); printf("**** 4.pnum ****\n"); printf("**** 5.addr ****\n"); printf("******************************\n"); } int sort_by_name(const void *s1, const void *s2) { return strcmp(((Data *) s1)->name, ((Data *) s2)->name); } int sort_by_sex(const void *s1, const void *s2) { return strcmp(((Data *) s1)->sex, ((Data *) s2)->sex); } int sort_by_age(const void *s1, const void *s2) { return ((Data *) s1)->age - ((Data *) s2)->age; } int sort_by_pnum(const void *s1, const void *s2) { return strcmp(((Data *) s1)->pnum, ((Data *) s2)->pnum); } int sort_by_addr(const void *s1, const void *s2) { return strcmp(((Data *) s1)->addr, ((Data *) s2)->addr); } //排序联系人 void ContactSort(Contact *p) { assert(p); int choice; sort_menu(); printf("请选择排序的参考量:>"); scanf("%d", &choice); switch (choice) { case NAME: qsort(p->data, p->size, sizeof(Data), sort_by_name); break; case SEX: qsort(p->data, p->size, sizeof(Data), sort_by_sex); break; case AGE: qsort(p->data, p->size, sizeof(Data), sort_by_age); break; case PNUM: qsort(p->data, p->size, sizeof(Data), sort_by_pnum); break; case ADDR: qsort(p->data, p->size, sizeof(Data), sort_by_addr); break; default: printf("输入有误,请检查输入!\n"); } } //打印帮助信息 void ContactHelp(Contact *p) { printf("*******************************************\n"); printf("****** add ---- 添加联系人信息 ******\n"); printf("****** del ---- 删除联系人信息 ******\n"); printf("****** search ---- 查找联系人信息 ******\n"); printf("****** modify ---- 修改联系人信息 ******\n"); printf("****** show ---- 展示联系人信息 ******\n"); printf("****** help ---- 帮助信息 ******\n"); printf("****** sort ---- 排序联系人信息 ******\n"); printf("****** exit ---- 退出通讯录 ******\n"); printf("*******************************************\n"); } /*//退出通讯录 void ContactExit(Contact *p) { printf("exit !\n"); }*/ //退出通讯录 --- 动态 void ContactExit(Contact *p) { //释放我们开辟的内存 free(p->data); printf("exit !\n"); }
main.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.help 0.exit ******\n"); printf("******************************************\n"); } void test() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); int input = 0;//存放用户选择的信息 do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); switch (input) { case ADD: ContactAdd(p); break; case DEL: ContactDel(p); break; case SEARCH: ContactSearch(p); break; case MODIFY: ContactModify(p); break; case SHOW: ContactShow(p); break; case SORT: ContactSort(p); break; case HELP: ContactHelp(p); break; case EXIT: ContactExit(p); break; default: printf("输入非法!\n"); } } while (input); } void test2() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); //用一个函数指针数组来存放函数指针 void (*fun[])(Contact *) ={ContactExit, ContactAdd, ContactDel, ContactSearch, ContactModify, ContactShow, ContactSort, ContactHelp}; int input = 0;//存放用户选择的信息 do{ menu(); printf("请输入你的选择:>"); scanf("%d", &input); if(input>=0&&input<=sizeof(fun)) { //system("cls"); fun[input](p); } else { system("cls"); printf("输入非法,请检查输入!\n"); } }while(input); } int main() { //test(); test2(); return 0; } #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.help 0.exit ******\n"); printf("******************************************\n"); } void test() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); int input = 0;//存放用户选择的信息 do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); switch (input) { case ADD: ContactAdd(p); break; case DEL: ContactDel(p); break; case SEARCH: ContactSearch(p); break; case MODIFY: ContactModify(p); break; case SHOW: ContactShow(p); break; case SORT: ContactSort(p); break; case HELP: ContactHelp(p); break; case EXIT: ContactExit(p); break; default: printf("输入非法!\n"); } } while (input); } void test2() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); //用一个函数指针数组来存放函数指针 void (*fun[])(Contact *) ={ContactExit, ContactAdd, ContactDel, ContactSearch, ContactModify, ContactShow, ContactSort, ContactHelp}; int input = 0;//存放用户选择的信息 do{ menu(); printf("请输入你的选择:>"); scanf("%d", &input); if(input>=0&&input<=sizeof(fun)) { //system("cls"); fun[input](p); } else { system("cls"); printf("输入非法,请检查输入!\n"); } }while(input); } int main() { //test(); test2(); return 0; }
三.带文件的动态通讯录
在这个里面,我们只需在初始化时进行文件的读取及关闭时文件的保存:
初始化:
//从文件载入信息 void Lodging(Contact *p) { assert(p); //打开一文件 FILE *fp = fopen("../Contact.dat", "ab");//如果不存在就创建,存在就追加 if (fp == NULL) { perror("FILE: Ab"); exit(-1); } fclose(fp);//关闭文件,我们这一步只是为了确保文件存在 //打开一文件 fp=fopen("../Contact.dat","rb"); if (fp == NULL) { perror("FILE: Rb"); exit(-1); } Data temp;//将读入的信息存入temp中 while(fread(&temp, sizeof(Data),1,fp)) { //检查容量 CheckCapacity(p); //赋值 p->data[p->size]=temp; p->size++; } fclose(fp);//关闭文件 } //初始化 --- 带文件 void ContactInit(Contact *p) { assert(p); p->data = NULL; p->size = 0; p->capacity = 1; Lodging(p); }
结束时保存:
void Save(Contact* p) { assert(p); FILE* fp =fopen("../Contact.dat","wb"); int i =0; for(i=0;i<p->size;i++) { fwrite(p->data+i, sizeof(Data),1,fp); } fclose(fp); } //退出通讯录 --- 带文件 void ContactExit(Contact *p) { //保存置文件 Save(p); //释放我们开辟的内存 free(p->data); printf("exit !\n"); }
除此之外,其他都与动态通讯录相同
完整代码:
Contact.h
//确保文件只包含一次 #ifndef CONTACT_CONTACT_H #define CONTACT_CONTACT_H #include <stdio.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <assert.h> //采用宏的目的是方便日后修改 #define NAME_MAX 20 #define SEX_MAX 8 #define PNUM_MAX 13 #define ADDR_MAX 20 #define MAX 10 //存放个人信息的结构体 typedef struct Data { char name[NAME_MAX];//姓名 int age;//年龄 char sex[SEX_MAX];//性别 char pnum[PNUM_MAX];//电话 char addr[ADDR_MAX];//地址 } Data; /* //存放MAX个个人信息的通讯录 --- 静态 typedef struct Contact { Data data[MAX]; int size; } Contact; */ //存放MAX个个人信息的通讯录 --- 动态 typedef struct Contact { Data* data;//存放数据 int size;//有效数据的个数 int capacity;//容量的大小 } Contact; //枚举来作为常量使得在看代码时比较清晰 enum choice { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, HELP }; enum sort_by { NAME=1, SEX, AGE, PNUM, ADDR }; //初始化通讯录 void ContactInit(Contact *p); //添加联系人 void ContactAdd(Contact* p); //删除联系人 void ContactDel(Contact* p); //查找联系人 void ContactSearch(Contact* p); //修改联系人信息 void ContactModify(Contact* p); //展示联系人信息 void ContactShow(Contact* p); //排序联系人 void ContactSort(Contact* p); //打印帮助 void ContactHelp(Contact* p); //退出通讯录 void ContactExit(Contact* p); //检查容量函数 void CheckCapacity(Contact *p); #endif //CONTACT_CONTACT_H
Contact.c
#include "Contact.h" // 强调!!! //调试请加setbuf(stdout,NULL)!!! //查找模块 int ContactFind(Contact *p, char *FindData) { assert(p); int i = 0; for (i = 0; i < p->size; i++) { if (strcmp(p->data[i].name, FindData) == 0) { return i;//找到就返回下标 } } return -1;//找不到就返回-1 } /* //初始化 --- 静态 void ContactInit(Contact *p) { //保证p不为NULL assert(p); //置零 memset(p->data, 0, sizeof(p->data)); p->size = 0; } */ /* //初始化 --- 动态 void ContactInit(Contact *p) { assert(p); p->data = NULL; p->size = 0; p->capacity = 1; } */ //从文件载入信息 void Lodging(Contact *p) { assert(p); //打开一文件 FILE *fp = fopen("../Contact.dat", "ab");//如果不存在就创建,存在就追加 if (fp == NULL) { perror("FILE: Ab"); exit(-1); } fclose(fp);//关闭文件,我们这一步只是为了确保文件存在 //打开一文件 fp=fopen("../Contact.dat","rb"); if (fp == NULL) { perror("FILE: Rb"); exit(-1); } Data temp;//将读入的信息存入temp中 while(fread(&temp, sizeof(Data),1,fp)) { //检查容量 CheckCapacity(p); //赋值 p->data[p->size]=temp; p->size++; } fclose(fp);//关闭文件 } //初始化 --- 带文件 void ContactInit(Contact *p) { assert(p); p->data = NULL; p->size = 0; p->capacity = 1; Lodging(p); } /*//添加联系人 --- 静态 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人容量已经等于最大容量了 if (p->size == MAX) { printf("通讯录已满,请删除一些后继续添加!\n"); return; } Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; }*/ //检查容量函数 void CheckCapacity(Contact *p) { assert(p); //如果联系人个数为0或与容量相同,就需要扩容 if (p->size == 0 || p->size == p->capacity) { //动态内存开辟 Data *ptr = (Data *) realloc(p->data, sizeof(Data) * p->capacity * 2); if (ptr == NULL)//开辟失败 { //报错 perror("CHECK CAPACITY ERROE !\n"); exit(-1); } //开辟成功,重新赋值 p->data = ptr; //扩容之后,容量也相应扩大 p->capacity *= 2; } //反之什么都不需要干 } //添加联系人 --- 动态 void ContactAdd(Contact *p) { //断言保证p不为NULL assert(p); //如果联系人个数等于容量,或联系人个数等于0,这时我们就需要扩容了,我们来使用一个函数来干这事 CheckCapacity(p); Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[p->size] = person; p->size++; } //删除联系人 void ContactDel(Contact *p) { assert(p); char DelName[NAME_MAX] = {0}; printf("请输入你要删除的联系人姓名:>"); scanf("%s", DelName); int ret = ContactFind(p, DelName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { int j = 0; for (j = ret; j < p->size; j++) { //从前往后依次挪动覆盖 p->data[j] = p->data[j + 1]; } //删除完成之后,联系人个数减一 p->size--; } } //查找联系人 void ContactSearch(Contact *p) { assert(p); char SearchName[NAME_MAX]; printf("请输入你要查找的联系人姓名:>"); scanf("%s", SearchName); //查找有无此人 int ret = ContactFind(p, SearchName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { printf("你所查找的联系人信息为:\n"); printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[ret].name, p->data[ret].sex, p->data[ret].age, p->data[ret].pnum, p->data[ret].addr); } } //修改联系人信息 void ContactModify(Contact *p) { assert(p); char ModifyName[NAME_MAX]; printf("请输入你要修改的联系人姓名:>"); scanf("%s", ModifyName); int ret = ContactFind(p, ModifyName); if (ret == -1) { printf("通讯录中并无此人,请重新检查输入!\n"); } else { Data person;//记录联系人信息 printf("请输入联系人的姓名:>"); scanf("%s", person.name); printf("请输入联系人的年龄:>"); scanf("%d", &person.age); printf("请输入联系人的性别:>"); scanf("%s", person.sex); printf("请输入联系人的电话:>"); scanf("%s", person.pnum); printf("请输入联系人的住址:>"); scanf("%s", person.addr); //将联系人信息存到通讯录中 p->data[ret] = person; } } //展示联系人信息 void ContactShow(Contact *p) { assert(p); if (p->size == 0) { printf("通讯录中并无一人!\n"); return; } int i = 0; printf(" 姓名\t性别\t 年龄\t 电话\t\t地址\n"); for (i = 0; i < p->size; i++) { printf(" %-5s\t%s\t%d\t%s\t%s\n", p->data[i].name, p->data[i].sex, p->data[i].age, p->data[i].pnum, p->data[i].addr); } } void sort_menu() { printf(" SORT_MENU \n"); printf("******************************\n"); printf("**** 1.name ****\n"); printf("**** 2.sex ****\n"); printf("**** 3.age ****\n"); printf("**** 4.pnum ****\n"); printf("**** 5.addr ****\n"); printf("******************************\n"); } int sort_by_name(const void *s1, const void *s2) { return strcmp(((Data *) s1)->name, ((Data *) s2)->name); } int sort_by_sex(const void *s1, const void *s2) { return strcmp(((Data *) s1)->sex, ((Data *) s2)->sex); } int sort_by_age(const void *s1, const void *s2) { return ((Data *) s1)->age - ((Data *) s2)->age; } int sort_by_pnum(const void *s1, const void *s2) { return strcmp(((Data *) s1)->pnum, ((Data *) s2)->pnum); } int sort_by_addr(const void *s1, const void *s2) { return strcmp(((Data *) s1)->addr, ((Data *) s2)->addr); } //排序联系人 void ContactSort(Contact *p) { assert(p); int choice; sort_menu(); printf("请选择排序的参考量:>"); scanf("%d", &choice); switch (choice) { case NAME: qsort(p->data, p->size, sizeof(Data), sort_by_name); break; case SEX: qsort(p->data, p->size, sizeof(Data), sort_by_sex); break; case AGE: qsort(p->data, p->size, sizeof(Data), sort_by_age); break; case PNUM: qsort(p->data, p->size, sizeof(Data), sort_by_pnum); break; case ADDR: qsort(p->data, p->size, sizeof(Data), sort_by_addr); break; default: printf("输入有误,请检查输入!\n"); } } //打印帮助信息 void ContactHelp(Contact *p) { printf("*******************************************\n"); printf("****** add ---- 添加联系人信息 ******\n"); printf("****** del ---- 删除联系人信息 ******\n"); printf("****** search ---- 查找联系人信息 ******\n"); printf("****** modify ---- 修改联系人信息 ******\n"); printf("****** show ---- 展示联系人信息 ******\n"); printf("****** help ---- 帮助信息 ******\n"); printf("****** sort ---- 排序联系人信息 ******\n"); printf("****** exit ---- 退出通讯录 ******\n"); printf("*******************************************\n"); } /*//退出通讯录 void ContactExit(Contact *p) { printf("exit !\n"); }*/ /* //退出通讯录 --- 动态 void ContactExit(Contact *p) { //释放我们开辟的内存 free(p->data); printf("exit !\n"); }*/ void Save(Contact* p) { assert(p); FILE* fp =fopen("../Contact.dat","wb"); int i =0; for(i=0;i<p->size;i++) { fwrite(p->data+i, sizeof(Data),1,fp); } fclose(fp); } //退出通讯录 --- 带文件 void ContactExit(Contact *p) { //保存置文件 Save(p); //释放我们开辟的内存 free(p->data); printf("exit !\n"); }
main.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.help 0.exit ******\n"); printf("******************************************\n"); } void test() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); int input = 0;//存放用户选择的信息 do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); switch (input) { case ADD: ContactAdd(p); break; case DEL: ContactDel(p); break; case SEARCH: ContactSearch(p); break; case MODIFY: ContactModify(p); break; case SHOW: ContactShow(p); break; case SORT: ContactSort(p); break; case HELP: ContactHelp(p); break; case EXIT: ContactExit(p); break; default: printf("输入非法!\n"); } } while (input); } void test2() { Contact list;//定义一个通讯录 Contact *p = &list;//赋址 //初始化 ContactInit(p); //用一个函数指针数组来存放函数指针 void (*fun[])(Contact *) ={ContactExit, ContactAdd, ContactDel, ContactSearch, ContactModify, ContactShow, ContactSort, ContactHelp}; int input = 0;//存放用户选择的信息 do{ menu(); printf("请输入你的选择:>"); scanf("%d", &input); if(input>=0&&input<=sizeof(fun)) { //system("cls"); fun[input](p); } else { system("cls"); printf("输入非法,请检查输入!\n"); } }while(input); } int main() { //test(); test2(); return 0; }