🌟一、明确通讯录信息/步骤
🌏1.1.通讯录信息
要写一个通讯录首先我们应该明确通讯录信息,然后才可以根据信息来写一个通讯录。
实现一个通讯录:
1.可以存放100个人的信息;
2.每个人的信息:
姓名;
性别;
年龄;
电话;
地址;
3.对信息的操作:
增加联系人;
删除指定联系人;
查找指定联系人;
修改指定联系人;
显示联系人信息;
排序联系人(按名字/年龄);
🌏1.2.通讯录多文件写法的好处
其次我们应该使用多文件的写法,避免写在一个文件里。
其好处至少可以说出以下几点:
1、方便代码复用
2、方便分工合作
3、方便后续维护
4、保证了库支持
🌏1.3.通讯录多文件写法
所以我们创建三个文件夹分别是:
test.c — 测试通讯录的相关功能
contact.c — 通讯录的实现模块
contact.h — 声明
🌟二、通讯录的实现
🌏2.1.通讯录的菜单部分
看过之前三子棋的都知道,通讯录也需要菜单供用户使用查看
因为我们需要按照上面规定的信息进行操作,所以我们菜单也要写上上面的信息。
分别是增加,删除,查找,修改,显示,排序,最后加上一个退出
void meau() { printf("*****************************\n"); printf("****1. add 2. del ****\n"); printf("****3. search 4. modify****\n"); printf("****5. show 6. sort ****\n"); printf("****0. exit ****\n"); printf("*****************************\n"); }
🌏2.2.通讯录的框架部分
对于用户使用通讯录应该是一个循环部分,所以我们可以使用do…while循环,然后采用switch语句来完善循环,以便用户选择,按照平常对switch的了解和使用,我们一般会采用,case1:,case2:等等来完成选择,但是这样会使代码的可读性不高,为什么呢?试想一下,用户选择1时候,还要查看上面的add del等产生不必要的麻烦,这就需要用到我们上章节学到的枚举类型了,大家不要忘了它会自己设定值这样我们在switch语句中,直接使用case ADD等
enum Option//结构体 { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT }; int main() { int input = 0; InitContact(&con); do { meau(); printf("请选择:>\n"); scanf("%d", &input); switch (input) { case ADD: break; case DEL: break; case SEARCH: break; case MODIFY: break; case SHOW: break; case SORT: break; case EXIT: printf("退出通讯录\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }
然后我们需要定义一个关于人的结构体的信息把它放到头文件里,这样别人使用或者我们使用的时候只需要包含头文件就可以了,为简便我们可以使用 (typedef)为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称,为PeoInfo
typedef struct PeoInfo { char name[20]; int age; char sex[5]; char tele[12]; char addr[30]; }PeoInfo;
我们希望我们的main函数运行起来显示一个通讯录,通讯录中可以存放100个人的信息,所以我们应该在test.c的测试部分的main函数中创建一个数组来存放
可以看到我们这里显示错误,错误信息为未定义标识符,所以我们一定要记得在test.c文件中包含#include"contact.h";
我们这个数组可以存放100个人的信息,我们可能对里面增加信息修改信息等一系列操作,所以我们应该知道这个通讯录中有几个人的信息 (注意哦100只是最大存放值是100我们增加信息时可能到达不了100所以需要操作时记录当时的人的信息数量),这就需要我们创建一个变量来记录
随着我们增加通讯录中的人的数量或者删除,这个sz也随之变化,所以我们增加一个到通讯录时,既要改变数组PeoInfo date[100]数组,也要改变sz,我们可以试想而知当我们使用函数操作时,既要传数组也要传sz有点麻烦那我们有没有更好的方法呢?
我们知道数组中存放的是信息,sz存放是数量,整合在一起刚好是一个通讯录,所以我们再次创建一个结构体放在contact.h中,里面是我们的这两个数据
typedef struct Contact { PeoInfo data[100];//存放数据 int sz;//记录通讯录中有效信息个数 }Contact;
然后我们再在text.c中实现一下:
到目前为止我们的通讯录整体结构写的差不多了,你有没有发现我们的数组中存放数据多是数字,当我们某一天想改变通讯录中的数量时,比如改为1000,那是不是需要在代码中找到数字然后更改有点麻烦这时就需要用到我们的定义(#define)了放在头文件中以便其他文件使用。
#define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 30
typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo; typedef struct Contact { PeoInfo data[MAX]; int sz; }Contact;
🌏2.3.通讯录的系列操作
📒(1).初始化
首先我们需要对通讯录初始化 (在contact.c中实现)注意也要在contact.x中包含#include"contact.h"头文件,保证里面的数据是从0开始的,所以我们需要分装一个函数,这样我们才可以正常的实行系列操作。
说的函数我们就会想起传值;传址两种操作,那我们传值可不可以呢?
很显然答案是不行的:传值调试时我们可以看到里面的值并没有改变;当我们传值的时候会有一份con的临时拷贝在里面进行相应操作修改时,是不会影响这里的con;所以我们最终采用的是传址。
注意:
data是一个连续的空间,不能直接pc->data的data是一个数组是一块连续的空间,数组名是地址不可以改成0所以应该把pc所指向通讯录的data数组里的值改成0,这里我们还需要用到之前学到的一个函数memset.
void InitContact(Contact* pc) { pc->sz=0; //pc->date;//date是一个数组是一块连续的空间,数组名是地址不可以改成0 //所以应该把pc所指向通讯录的date数组里的值改成0 memset(pc->data, 0, sizeof(pc->data)); }
📒(2).增加联系人
首先在contact.h中声明一下,然后再contact.c中实现
在contact.c中实现的时候这里有一点容易忽略:通讯录如果满了怎么办还需要增加吗?所以我们应该判断一下:
还需要注意什么时候需要&操作,数组名本来就是一个地址所以不需要进行&操作;年龄是一个变量所以需要&操作;还要注意
结构体对象用 .
结构体指针用 ->
void AddContact(Contact* pc) { if (pc->sz == MAX) { printf("通讯录已满,无法增加\n"); return;//在函数里面遇见return就返回了,因为是void所以不需要返回值 } printf("请输入名字>:"); //结构体对象用 . //结构体指针用 -> scanf("%s", pc->data[pc->sz].name);//数组名本来就是一个地址所以不需要进行&操作 printf("请输入年龄>:"); scanf("%d", &(pc->data[pc->sz].age));//年龄是一个变量所以需要&操作 printf("请输入性别>:"); scanf("%s", pc->data[pc->sz].sex ); printf("请输入电话>:"); scanf("%s", pc->data[pc->sz].tele ); printf("请输入地址>:"); scanf("%s", pc->data[pc->sz].addr ); pc->sz++; printf("输入完成\n"); }
📒(3).显示联系人
我们添加完成后打印出来看看是否成功输入所以需要运用show操作;然后我们设置一个函数ShowContact(&con),同样传址操作,打印时不是不可以传值,传值也是可以的哦~
然后在contact.h中声明,在contact.c中实现
我们希望打印出来是下面格式:
姓名 年龄 性别 电话 地址
zhangsan 20 男 1234567890 北京
打印要根据里面的人物信息的输入个数所以i< pc->sz;打印时我们可以设置长度,例如%5s;
%5s是把变量的值保持长度5位(不足5位时),不足5位在前面用空格补齐,超过5位就不用补空格,直接显示全部,以字符串方式输出。也就是所谓的右对齐
%-5s 是把变量的值保持长度5位(不足5位时),不足5位在后面用空格补齐,超过5位就不用补空格,直接显示全部,以字符串方式输出。也就是所谓的左对齐
为了美观我们采用左对齐所以前面要加负号,打印时标题和数据要保持一致这样才能更美观
void ShowContact(Contact* pc) { int i = 0; //printf("%10s %4s %5s %12s %30s\n", "姓名" "年龄" "性别" "电话" "地址"); //打印标题 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印数据 for (i = 0; i < pc->sz; i++) { printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); }
📒(4).运行结果
📒(5).查找联系人
这里可以向各位在提供一种写法
typedef struct Contact { PeoInfo data[MAX]; int sz; }* pContact;//这里是一种指针 void DelContact(pContact pc);//我们这样写也可以哦~
首先在contact.h中声明一下:
然后在contact.c中实现:
我们怎么实现呢?首先输入指定联系人姓名,然后遍历数组用strcmp进行比较如果==0时就找到了我们就用一个变量来记录这个位置找到就退出,如果没有也就退出
void DelContact(Contact* pc) { char name[MAX_NAME]={0}; int pos = 0; printf("请输入要删除指定联系人的名字\n"); scanf("%s", &name); if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); } //删除 //1.找到删除的指定联系人 - 位置 (下标) int i = 0; for (i = 0; i < pc->sz; i++) { if(strcmp(pc->data [i].name ,name)==0) { pos = i;//记录所删除联系人所在位置的下标 break; } } if (i==pc->sz )//这里如果判断pos==0时可能是第一个元素因为i是从0开始了所以可以把pos开始赋值为-1 { printf("要删除的人不存在\n"); return; } //2.删除 - 删除pos位置上的数据 for (i = pos;i<pc->sz -1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--;//元素个数减少 printf("删除成功\n"); }
我们发现下面的查找也需要遍历删除也需要所以为了简洁我们可以把这个遍历的数组分装成一个函数
int FindByName(Contact*pc,char name[]) { int pos = FindByName(pc, name); int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i; break; } } if (i == pc->sz) { return -1; } }
void DelContact(Contact* pc) { char name[MAX_NAME]={0}; int i = 0; int pos = 0; printf("请输入要删除指定联系人的名字\n"); scanf("%s", &name); if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); } //删除 //1.找到删除的指定联系人 - 位置 (下标) int pos = FindByName(pc, name); if (pos == -1) { printf("要删除的人不存在\n"); } //2.删除 - 删除pos位置上的数据 for (i = pos;i<pc->sz -1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--;//元素个数减少 printf("删除成功\n"); }
📒(6).修改联系人信息
首先在contact.h中声明一下:
然后在contact.c中实现:
void ModifyContact(Contact* pc) { char name[MAX_NAME] = { 0 }; printf("请输入修改指定联系人的名字\n"); scanf("%s", &name); int pos = FindByName(pc, name); if (pos == -1) { printf("要修改的人不存在\n"); } //存在就修改 printf("请输入名字>:"); scanf("%s", pc->data[pos].name); printf("请输入年龄>:"); scanf("%d", &(pc->data[pos].age)); printf("请输入性别>:"); scanf("%s", pc->data[pos].sex); printf("请输入电话>:"); scanf("%s", pc->data[pos].tele); printf("请输入地址>:"); scanf("%s", pc->data[pos].addr); printf("修改成功\n"); }
📒(7).排序联系人
假设根据联系人姓名排序我们就需要用到之前学过的一个库函数(qsort)
首先在contact.h中声明一下:
然后在contact.c中实现:
int cmp_by_name(const void* e1, const void* e2) { return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name); } void SortContact(Contact* pc) { qsort(pc->data, pc->sz, sizeof(PeoInfo),cmp_by_name); printf("排序成功\n"); }
9如果对qsort函数不太了解的话可以看C语言学习第十八弹:
🌟三、通讯录的全部代码
contact.h中的全部代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 30 //表示一个人信息 typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo; typedef struct Contact { PeoInfo data[MAX]; int sz; }Contact; //typedef struct Contact //{ // PeoInfo data[MAX]; // int sz; //}* pContact; //初始化通讯录 void InitContact(Contact* pc); //增加指定联系人 void AddContact(Contact* pc); //显示联系人信息 void ShowContact(Contact* pc); //删除联系人信息 //void DelContact(pContact pc); void DelContact(Contact* pc); //查找指定联系人 void SearchContact(Contact* pc); //修改指定联系人信息 void ModifyContact(Contact* pc); //排序联系人 void SortContact(Contact* pc);
contact.c中的全部代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include"contact.h" #include<string.h> //void InitContact(Contact c) //{ // c.az; //} void InitContact(Contact* pc) { pc->sz=0; //pc->date;//date是一个数组是一块连续的空间,数组名是地址不可以改成0 //所以应该把pc所指向通讯录的date数组里的值改成0 memset(pc->data, 0, sizeof(pc->data)); } void AddContact(Contact* pc) { if (pc->sz == MAX) { printf("通讯录已满,无法增加\n"); return;//在函数里面遇见return就返回了,因为是void所以不需要返回值 } printf("请输入名字>:"); //结构体对象用 . //结构体指针用 -> scanf("%s", pc->data[pc->sz].name);//数组名本来就是一个地址所以不需要进行&操作 printf("请输入年龄>:"); scanf("%d", &(pc->data[pc->sz].age));//年龄是一个变量所以需要&操作 printf("请输入性别>:"); scanf("%s", pc->data[pc->sz].sex ); printf("请输入电话>:"); scanf("%s", pc->data[pc->sz].tele ); printf("请输入地址>:"); scanf("%s", pc->data[pc->sz].addr ); pc->sz++; printf("输入完成\n"); } void ShowContact(const Contact* pc) { //姓名 年龄 性别 电话 地址 //zhangsan 20 男 1234567890 北京 int i = 0; //printf("%10s %4s %5s %12s %30s\n", "姓名" "年龄" "性别" "电话" "地址"); //打印标题 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印数据 for (i = 0; i < pc->sz; i++) { printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data [i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } } static int FindByName(Contact*pc,char name[])//加上static这个函数只能在所在.c文件中使用 { int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i;//记录所删除联系人所在位置的下标 break; } } if (i == pc->sz)//这里如果判断pos==0时可能是第一个元素因为i是从0开始了所以可以把pos开始赋值为-1 { return -1; } } void DelContact(Contact* pc) { char name[MAX_NAME] = { 0 }; int i = 0; printf("请输入要删除指定联系人的名字\n"); scanf("%s", &name); if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); } //删除 //1.找到删除的指定联系人 - 位置 (下标) int pos = FindByName(pc, name); if (pos == -1) { printf("要删除的人不存在\n"); } //2.删除 - 删除pos位置上的数据 for (i = pos;i<pc->sz -1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--;//元素个数减少 printf("删除成功\n"); } void SearchContact(Contact* pc) { //我们发现查找也需要遍历删除也需要所以为了简洁我们可以把这个遍历的数组分装成一个函数 char name[MAX_NAME] = { 0 }; printf("请输入要查找人的名字\n"); scanf("%s", &name); int pos = FindByName(pc, name); if (pos == -1) { printf("要查找的人不存在\n"); } //找到就打印 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } void ModifyContact(Contact* pc) { char name[MAX_NAME] = { 0 }; printf("请输入修改指定联系人的名字\n"); scanf("%s", &name); int pos = FindByName(pc, name); if (pos == -1) { printf("要修改的人不存在\n"); } //存在就修改 printf("请输入名字>:"); scanf("%s", pc->data[pos].name); printf("请输入年龄>:"); scanf("%d", &(pc->data[pos].age)); printf("请输入性别>:"); scanf("%s", pc->data[pos].sex); printf("请输入电话>:"); scanf("%s", pc->data[pos].tele); printf("请输入地址>:"); scanf("%s", pc->data[pos].addr); printf("修改成功\n"); } //按照名字排序 int cmp_by_name(const void* e1, const void* e2) { return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name); } void SortContact(Contact* pc) { qsort(pc->data, pc->sz, sizeof(PeoInfo),cmp_by_name); printf("排序成功\n"); }
test.c中的全部代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include "contact.h" void meau() { printf("*****************************\n"); printf("****1. add 2. del ****\n"); printf("****3. search 4. modify****\n"); printf("****5. show 6. sort ****\n"); printf("****0. exit ****\n"); printf("*****************************\n"); } enum Option//结构体 { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT }; int main() { int input = 0; //PeoInfo date[100]; //int sz = 0;//记录通讯录中有几个人的信息 Contact con; //初始化信息 //传值调试时我们可以看到里面的值并没有改变 //InitContact(con);//当我们传值的时候会有一份con的临时拷贝在里面进行相应操作修改时,是不会影响这里的con InitContact(&con); do { meau(); printf("请选择:>\n"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con);//输入信息,所输入的信息最终还是要放入通讯录所以要传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 EXIT: printf("退出通讯录\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }
😽总结
😽Ending,今天的实现静态通讯录内容就到此结束啦~如果后续想了解更多,就请关注我吧