- 初始化通讯录函数InitContact
- 添加通讯录函数AddContact
- 显示通讯录函数ShowContact
- 找人函数FindByName
- 删除指定联系人函数DelContact
- 查找联系人函数SearchContact
- 修改指定联系人函数ModifyContact
- 快排通讯录函数(年龄排序)QsortContact
丐版通讯录
什么是丐版,就是能达到基本温饱,而微星的丐版是性价比最高的丐版,当然我是高配哈哈哈,这个通讯录就是微星丐版,一些基本用户功能是有的,甚至还多了功能,排序,用户级别的控制内存,当然有些用户友好功能没写(例如输错三次强制退出啊什么的),这些得高配再写,不然就不叫丐版了,我相信大多数人用了没有说不好的,那些高配的不要来装了(文件处理的哈哈哈),高配我们家也有,不过还没到发售时间。不吹了,吃饭了
通讯录他能干什么呢
1.存放1000个好友信息
那么一个人的好友信息包含什么东西呢
包含姓名,电话,性别,住址,年龄
2.还可以增加好友信息
3.删除指定名字的好友信息
4.查找好友信息
5.修改好友信息
6.打印好友信息
7.排序好友信息
静态版本
test.c
测试通讯录各个功能
main.c
首先就是主函数
int main() { int input = 0; //int size = 0;//用来记录通讯录有多少人的信息变量 //struct PeoInfo con[MAX];//存放1000个人的信息 /*为了下面函数不重复的写con,&size,就把他们集合在 一起了struct Contact*/ struct Contact con;//con就是通讯录,里面包含1000人信息的数组,和size InitContact(&con);//初始化通讯录 do//首先让他循环起来 { menu();//显示菜单 printf("请选择:>"); 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: QsortContact(&con); break; case EXIT: printf("退出通讯录\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }
菜单
void menu() { printf("*******************************\n"); printf("***** 1.add 2.del ***\n"); printf("***** 3.search 4.modify ***\n"); printf("***** 5.show 6.short ***\n"); printf("***** 0.exit ***\n"); printf("*******************************\n"); }
这里的菜单序号要想到枚举
contact.c
通讯录的实现
初始化通讯录函数InitContact
//初始化通讯录 void InitContact(struct Contact* ps) { assert(ps); memset(ps->data, 0, sizeof(ps->data));//把data这个空间里全部设置为零 ps->size = 0;//让我们通讯录最初只有0个元素 }
添加通讯录函数AddContact
//添加通讯录 void AddContact(struct Contact* ps) { assert(ps); if (ps->size == MAX)//通讯录满了就添加不了信息 { printf("通讯录已满无法添加\n"); } else { printf("请输入名字:"); scanf("%s", ps->data[ps->size].name);//ps->data找到那个数组, //ps->size找到放在[]里面就是 找到数组里面的某一个元素了,找到后再.name放到名字里面去 printf("请输入电话:"); scanf("%s", ps->data[ps->size].tele); printf("请输入性别:"); scanf("%s", ps->data[ps->size].sex); printf("请输入地址:"); scanf("%s", ps->data[ps->size].addr); printf("请输入年龄:"); scanf("%d", &(ps->data[ps->size].age));//这里和上面不一样,因为上面是字符串,名字就是首地址 //而age是int 所以得取地址 //填好之后size得加加,因为size既是通讯录数组下标,也是人数的标准,添加一人就++; ps->size++; printf("添加成功\n"); } }
显示通讯录函数ShowContact
//不知道自己操作成没成功就show一下 //显示通讯录 void ShowContact(const struct Contact* ps)//这里为了安全,我们只是看一下里面的内容,不会修改,所以就const { assert(ps); if (!ps->size) { printf("通讯录里面没有人,没什么好显示的:\n"); } else { int i = 0; printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n","名字","年龄","性别","电话","地址"); for (i = 0; i < ps->size; i++) { printf("%-10s\t%-5d\t%-5s\t%-12s\t%-20s\n",//为了对齐就把上面的格式拿下来,注意年龄的格式s变成了d ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele, ps->data[i].addr); } } }
找人函数FindByName
//只给本文件内函数用 static int FindByName(const struct Contact* ps, char name[MAX_NAME]) { assert(ps); int i = 0; for (i = 0; i < ps->size; i++) { if (0 == strcmp(ps->data[i].name, name)) { //break;//找到就跳出来 return i; } } return -1; }
删除指定联系人函数DelContact
//删除指定的联系人 void DelContact(struct Contact* ps) { assert(ps); if (ps->size == 0) { printf("通讯录为空,无法删除.\n"); return 0; } char name[MAX_NAME] = {0}; printf("请输入要删除人的姓名:"); scanf("%s", name); int pos = FindByName(ps,name);//找到把下标拿出来,找不到返回-1 查找要删除的人在什么位置 高校做法也就是打工人做法,万物皆遍历 //int i = 0; //for (i = 0; i < ps->size; i++) //{ // if (0 == strcmp(ps->data[i].name, name)) // { // break;//找到就跳出来 // } //} //然后再删除 if (pos == -1)//说明他遍历完了也没找到那个名字,也就是说是不存在的 { printf("要删除的人不存在:"); } else { //删除数据 int j = 0; for (int j = pos; j < ps->size; j++) { ps->data[j] = ps->data[j + 1];//需要删除的数据后面的来补删除数据的空间 } ps->size--;//删除了,人就减一 printf("删除成功"); } }
查找联系人函数SearchContact
//查找联系人 我就是查找不修改,所以要const void SearchContact(const struct Contact* ps) { assert(ps); char name[MAX_NAME] = { 0 }; printf("请输入 需要查找人的名字:"); scanf("%s",name); 查找的过程和删除的 查找过程一样 ,这时候再写一样的话就会出现代码冗余,所以我们需要写一个通用的查找功能 //int i = 0; //for (i = 0; i < ps->size; i++) //{ // if (0 == strcmp(ps->data[i].name, name)) // { // break;//找到就跳出来 // } //} int pos = FindByName(ps, name); if (pos == -1)//说明他遍历完了也没找到那个名字,也就是说是不存在的 { printf("要查找的人不存在:"); } else { //找到就打印 printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址"); printf("%-10s\t%-5d\t%-5s\t%-12s\t%-20s\n",//为了对齐就把上面的格式拿下来,注意年龄的格式s变成了d ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex, ps->data[pos].tele, ps->data[pos].addr); } }
修改指定联系人函数ModifyContact
//修改指定联系人 void ModifyContact( struct Contact* ps) { assert(ps); char name[MAX_NAME] = { 0 }; printf("请输入要修改人的名字:"); scanf("%s",name); int pos = FindByName(ps,name); if (pos == -1) { printf("没有修改人的名字\n"); } else { printf("请输入名字:"); scanf("%s", ps->data[pos].name);//ps->data找到那个数组, //ps->size找到放在[]里面就是 找到数组里面的某一个元素了,找到后再.name放到名字里面去 printf("请输入电话:"); scanf("%s", ps->data[pos].tele); printf("请输入性别:"); scanf("%s", ps->data[pos].sex); printf("请输入地址:"); scanf("%s", ps->data[pos].addr); printf("请输入年龄:"); scanf("%d", &(ps->data[pos].age)); printf("修改完成:"); } }
快排通讯录函数(年龄排序)QsortContact
//由于中文名在内存中存储方式的问题。所以我们选择年龄排序 static int cmp_peoinfo_age(const void* e1, const void* e2) { return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age; } //快排通讯录 void QsortContact(struct Contact* ps) { assert(ps); if (ps->size < 2) { printf("所需排序人少于2人,无需排序"); return 0; } qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_peoinfo_age); }
contact.h
通讯录的声明
创建通讯录的一些宏
#define MAX 1000 #define MAX_NAME 20 //为了好修改直接宏定义 #define MAX_TELE 12 #define MAX_SEX 5 #define MAX_ADDR 30
选项(方便删改)
//选项,因为每次输入数字的话还要看看数字的意思,会很麻烦,所以枚举 enum Option { EXIT, //0退出 ADD, //1添加 DEL, //2删除 SEARCH, //3查找 MODIFY, //4修改 SHOW, //5查看 SORT //6排序 };
用户信息结构体
//创建用户信息类型包含姓名,电话,性别,住址,年龄 //然后通讯录就是1000个用户组成的,创建一个数组就好 struct PeoInfo { char name[MAX_NAME]; char tele[MAX_TELE]; char sex[MAX_SEX]; char addr[MAX_ADDR]; int age; };
通讯录结构体
//通讯录类型,里面是通讯录信息 struct Contact { struct PeoInfo data[MAX];//存放1000个信息 int size;//记录当前个数 };
声明函数
//声明函数 //初始化通讯录 void InitContact(struct Contact* ps); //添加通讯录 void AddContact(struct Contact* ps); //显示通讯录 void ShowContact(const struct Contact* ps); /*为了通过name查找人,这里用函数写是因为之后删除,查询,修改等都 需要用到find这个功能,写在函数里面的话很多都是重复的会显的很冗余 int FindByName(struct Contact* ps, char name[MAX_NAME]); 这个函数声明就不要声明了,不需要给用户, 我们给用户的功能就那菜单里的那几个,这个函数是为了服务查询名字而出现的,不是为了用户出现,所以不 需要暴露给用户,而且还要加上static,不让其他人用,只给文件 内函数用*/ //删除指定的联系人 void DelContact(struct Contact* ps); //查找联系人 void SearchContact(const struct Contact* ps); //修改指定联系人 void ModifyContact(struct Contact* ps); //排序联系人 void QsortContact(struct Contact* ps);
动态版本
比其他丐版的优势
contact.h
改一:通讯录结构体
//动态版本 //通讯录类型,里面是通讯录信息 struct Contact { struct PeoInfo* data;//用指针来维护用户空间 int sz; //记录通讯录当前的元素个数 int capacity ; //通讯录最大容量 };
contact.c
改二:初始化通讯录函数InitContact
//动态版本 //初始化通讯录 //刚开始的时候通讯录结构体就给了指针但没有指向空间,所以我们得初始化给他 //只有三个人的元素,不够再加呗 void InitContact(struct Contact* ps) { ps->sz = 0;//此时有效元素是0 ps->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));//先给3个元素 ps->capacity = DEFAULT_SZ; }
改三:增加通讯录函数AddContact
//动态版本 // 添加通讯录 void AddContact( struct Contact* ps) { //当有效元素个数和通讯录最大容量相等时就要扩容了 if (ps->sz == ps->capacity) { struct PeoInfo* ptr = (struct PeoInfo*)realloc(ps->data, (ps->capacity + 2) * sizeof(struct PeoInfo));//增加两个元素 if (ptr == NULL) { printf("扩容失败\n"); return 0; } else { ps->data = ptr;//ptr不为空就交给data维护 ps->capacity += 2;//这个不要忘了总容量也要加 printf("扩容成功\n"); } } //然后再增加新的信息 printf("请输入名字:"); scanf("%s", ps->data[ps->sz].name);//ps->data找到那个数组, //ps->size找到放在[]里面就是 找到数组里面的某一个元素了,找到后再.name放到名字里面去 printf("请输入电话:"); scanf("%s", ps->data[ps->sz].tele); printf("请输入性别:"); scanf("%s", ps->data[ps->sz].sex); printf("请输入地址:"); scanf("%s", ps->data[ps->sz].addr); printf("请输入年龄:"); scanf("%d", &(ps->data[ps->sz].age));//这里和上面不一样,因为上面是字符串,名字就是首地址 //而age是int 所以得取地址 //填好之后sz得加加,因为sz既是通讯录数组下标,也是人数的标准,添加一人就++; (ps->sz)++; printf("添加成功\n"); }
注意我们在扩容却没有释放,那我们什么时候释放呢,是不是只有程序结束也就是程序退出的时候释放,也就是Exit的时候
所以我们得写个销毁通讯录的函数
添加四:销毁通讯录函数DestroyContact
//销毁通讯录 void DestroyContact(struct Contact* ps) { //释放人员信息空间 free(ps->data); ps->data = NULL; //指向的数组都没了,所以sz,capacity也没用了 ps->sz = 0; ps->capacity = 0; }
完整代码
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"contact.h" void menu() { printf("*******************************\n"); printf("***** 1.add 2.del ***\n"); printf("***** 3.search 4.modify ***\n"); printf("***** 5.show 6.short ***\n"); printf("***** 0.exit ***\n"); printf("*******************************\n"); } int main() { int input = 0; //int size = 0;//用来记录通讯录有多少人的信息变量 //struct PeoInfo con[MAX];//存放1000个人的信息 /*为了下面函数不重复的写con,&size,就把他们集合在 一起了struct Contact*/ struct Contact con;//con就是通讯录,里面包含1000人信息的数组,和size InitContact(&con);//初始化通讯录 do//首先让他循环起来 { menu();//显示菜单 printf("请选择:>"); 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: QsortContact(&con); break; case EXIT: DestroyContact(&con); printf("退出通讯录\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }
contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"contact.h" 静态版本 初始化通讯录 //void InitContact(struct Contact* ps) //{ // assert(ps); // memset(ps->data, 0, sizeof(ps->data));//把data这个空间里全部设置为零 // ps->size = 0;//让我们通讯录最初只有0个元素 //} //动态版本 //初始化通讯录 //刚开始的时候通讯录结构体就给了指针但没有指向空间,所以我们得初始化给他 //只有三个人的元素,不够再加呗 void InitContact(struct Contact* ps) { ps->sz = 0;//此时有效元素是0 ps->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));//先给3个元素 ps->capacity = DEFAULT_SZ; } 静态版本 添加通讯录 //void AddContact(struct Contact* ps) //{ // assert(ps); // if (ps->size == MAX)//通讯录满了就添加不了信息 // { // printf("通讯录已满无法添加\n"); // } // else // { // printf("请输入名字:"); // scanf("%s", ps->data[ps->size].name);//ps->data找到那个数组, // //ps->size找到放在[]里面就是 找到数组里面的某一个元素了,找到后再.name放到名字里面去 // printf("请输入电话:"); // scanf("%s", ps->data[ps->size].tele); // printf("请输入性别:"); // scanf("%s", ps->data[ps->size].sex); // printf("请输入地址:"); // scanf("%s", ps->data[ps->size].addr); // printf("请输入年龄:"); // scanf("%d", &(ps->data[ps->size].age));//这里和上面不一样,因为上面是字符串,名字就是首地址 // //而age是int 所以得取地址 // //填好之后size得加加,因为size既是通讯录数组下标,也是人数的标准,添加一人就++; // ps->size++; // printf("添加成功\n"); // } //} //动态版本 // 添加通讯录 void AddContact( struct Contact* ps) { //当有效元素个数和通讯录最大容量相等时就要扩容了 if (ps->sz == ps->capacity) { struct PeoInfo* ptr = (struct PeoInfo*)realloc(ps->data, (ps->capacity + 2) * sizeof(struct PeoInfo));//增加两个元素 if (ptr == NULL) { printf("扩容失败\n"); return 0; } else { ps->data = ptr;//ptr不为空就交给data维护 ps->capacity += 2;//这个不要忘了总容量也要加 printf("扩容成功\n"); } } //然后再增加新的信息 printf("请输入名字:"); scanf("%s", ps->data[ps->sz].name);//ps->data找到那个数组, //ps->size找到放在[]里面就是 找到数组里面的某一个元素了,找到后再.name放到名字里面去 printf("请输入电话:"); scanf("%s", ps->data[ps->sz].tele); printf("请输入性别:"); scanf("%s", ps->data[ps->sz].sex); printf("请输入地址:"); scanf("%s", ps->data[ps->sz].addr); printf("请输入年龄:"); scanf("%d", &(ps->data[ps->sz].age));//这里和上面不一样,因为上面是字符串,名字就是首地址 //而age是int 所以得取地址 //填好之后sz得加加,因为sz既是通讯录数组下标,也是人数的标准,添加一人就++; (ps->sz)++; printf("添加成功\n"); } //销毁通讯录 void DestroyContact(struct Contact* ps) { //释放人员信息空间 free(ps->data); ps->data = NULL; //指向的数组都没了,所以sz,capacity也没用了 ps->sz = 0; ps->capacity = 0; } //不知道自己操作成没成功就show一下 //显示通讯录 void ShowContact(const struct Contact* ps)//这里为了安全,我们只是看一下里面的内容,不会修改,所以就const { assert(ps); if (!ps->sz) { printf("通讯录里面没有人,没什么好显示的:\n"); } else { int i = 0; printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n","名字","年龄","性别","电话","地址"); for (i = 0; i < ps->sz; i++) { printf("%-10s\t%-5d\t%-5s\t%-12s\t%-20s\n",//为了对齐就把上面的格式拿下来,注意年龄的格式s变成了d ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].tele, ps->data[i].addr); } } } //只给本文件内函数用 static int FindByName(const struct Contact* ps, char name[MAX_NAME]) { assert(ps); int i = 0; for (i = 0; i < ps->sz; i++) { if (0 == strcmp(ps->data[i].name, name)) { //break;//找到就跳出来 return i; } } return -1; } //删除指定的联系人 void DelContact(struct Contact* ps) { assert(ps); if (ps->sz == 0) { printf("通讯录为空,无法删除.\n"); return 0; } char name[MAX_NAME] = {0}; printf("请输入要删除人的姓名:"); scanf("%s", name); int pos = FindByName(ps,name);//找到把下标拿出来,找不到返回-1 查找要删除的人在什么位置 高校做法也就是打工人做法,万物皆遍历 //int i = 0; //for (i = 0; i < ps->size; i++) //{ // if (0 == strcmp(ps->data[i].name, name)) // { // break;//找到就跳出来 // } //} //然后再删除 if (pos == -1)//说明他遍历完了也没找到那个名字,也就是说是不存在的 { printf("要删除的人不存在:"); } else { //删除数据 int j = 0; for (int j = pos; j < ps->sz; j++) { ps->data[j] = ps->data[j + 1];//需要删除的数据后面的来补删除数据的空间 } ps->sz--;//删除了,人就减一 printf("删除成功"); } } //查找联系人 我就是查找不修改,所以要const void SearchContact(const struct Contact* ps) { assert(ps); char name[MAX_NAME] = { 0 }; printf("请输入 需要查找人的名字:"); scanf("%s",name); 查找的过程和删除的 查找过程一样 ,这时候再写一样的话就会出现代码冗余,所以我们需要写一个通用的查找功能 //int i = 0; //for (i = 0; i < ps->size; i++) //{ // if (0 == strcmp(ps->data[i].name, name)) // { // break;//找到就跳出来 // } //} int pos = FindByName(ps, name); if (pos == -1)//说明他遍历完了也没找到那个名字,也就是说是不存在的 { printf("要查找的人不存在:"); } else { //找到就打印 printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址"); printf("%-10s\t%-5d\t%-5s\t%-12s\t%-20s\n",//为了对齐就把上面的格式拿下来,注意年龄的格式s变成了d ps->data[pos].name, ps->data[pos].age, ps->data[pos].sex, ps->data[pos].tele, ps->data[pos].addr); } } //修改指定联系人 void ModifyContact( struct Contact* ps) { assert(ps); char name[MAX_NAME] = { 0 }; printf("请输入要修改人的名字:"); scanf("%s",name); int pos = FindByName(ps,name); if (pos == -1) { printf("没有修改人的名字\n"); } else { printf("请输入名字:"); scanf("%s", ps->data[pos].name);//ps->data找到那个数组, //ps->size找到放在[]里面就是 找到数组里面的某一个元素了,找到后再.name放到名字里面去 printf("请输入电话:"); scanf("%s", ps->data[pos].tele); printf("请输入性别:"); scanf("%s", ps->data[pos].sex); printf("请输入地址:"); scanf("%s", ps->data[pos].addr); printf("请输入年龄:"); scanf("%d", &(ps->data[pos].age)); printf("修改完成:"); } } //由于中文名在内存中存储方式的问题。所以我们选择年龄排序 static int cmp_peoinfo_age(const void* e1, const void* e2) { return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age; } //快排通讯录 void QsortContact(struct Contact* ps) { assert(ps); if (ps->sz < 2) { printf("所需排序人少于2人,无需排序"); return 0; } qsort(ps->data, ps->sz, sizeof(ps->data[0]), cmp_peoinfo_age); }
contact.h
#pragma once #include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #define MAX 1000 #define MAX_NAME 20 //为了好修改直接宏定义 #define MAX_TELE 12 #define MAX_SEX 5 #define MAX_ADDR 30 #define DEFAULT_SZ 3 //选项,因为每次输入数字的话还要看看数字的意思,会很麻烦,所以枚举 enum Option { EXIT, //0退出 ADD, //1添加 DEL, //2删除 SEARCH, //3查找 MODIFY, //4修改 SHOW, //5查看 SORT //6排序 }; //创建用户信息类型包含姓名,电话,性别,住址,年龄 //然后通讯录就是1000个用户组成的,创建一个数组就好 struct PeoInfo { char name[MAX_NAME]; char tele[MAX_TELE]; char sex[MAX_SEX]; char addr[MAX_ADDR]; int age; }; 静态版本 通讯录类型,里面是通讯录信息 //struct Contact //{ // struct PeoInfo data[MAX];//存放1000个信息 // int size;//记录当前个数 //}; //动态版本 //通讯录类型,里面是通讯录信息 struct Contact { struct PeoInfo* data;//用指针来维护用户空间 int sz; //记录通讯录当前的元素个数 int capacity ; //通讯录最大容量 }; //声明函数 //初始化通讯录 void InitContact(struct Contact* ps); //添加通讯录 void AddContact(struct Contact* ps); //显示通讯录 void ShowContact(const struct Contact* ps); /*为了通过name查找人,这里用函数写是因为之后删除,查询,修改等都 需要用到find这个功能,写在函数里面的话很多都是重复的会显的很冗余 int FindByName(struct Contact* ps, char name[MAX_NAME]); 这个函数声明就不要声明了,不需要给用户, 我们给用户的功能就那菜单里的那几个,这个函数是为了服务查询名字而出现的,不是为了用户出现,所以不 需要暴露给用户,而且还要加上static,不让其他人用,只给文件 内函数用*/ //删除指定的联系人 void DelContact(struct Contact* ps); //查找联系人 void SearchContact(const struct Contact* ps); //修改指定联系人 void ModifyContact(struct Contact* ps); //排序联系人 void QsortContact(struct Contact* ps); //销毁通讯录函数 void DestroyContact(struct Contact* ps);
跑图