目录
前言
一、整体逻辑分析
二、实现步骤
1、创建菜单和多次操作问题
2、创建通讯录
3、初始化通讯录
4、添加联系人
5、显示联系人
6、删除指定联系人
7、查找指定联系人
8、修改联系人信息
9、排序联系人信息
三、全部源码
前言
我们上期已经详细的介绍了自定义类型,本期我们来用学过的知识来做个小项目来练习练习。本期我们将用C语言实现通讯录静态版!后期介绍了动态内存管理和文件操作我们将会在此版本的基础上进行优化改进!
一、整体逻辑分析
我们还和以前实现三子棋和扫雷小项目一样,先对整体工程的逻辑尽可能详细的梳理,否则一开就是一顿狂敲,最后一走发现全是Bug这样就不好了!我们一开始尽可能的分析清楚,后面即使出现了问题也能快速确定!本期还是和往期一样采用多文件编程!OK,我们下面一起来梳理一下这个小项目的基本实现逻辑:
1、创建菜单和多次操作问题
2、创建通讯录
3、通讯录的初始化以及增删查改等操作
二、实现步骤
1、创建菜单和多次操作问题
我们期望当我们进行对通讯录操作时可以给我们在进行操作前给一个菜单功能我们操作!考虑到后面还要多次操作通讯录(增删查改等)我们结合起来很快想到用do..while循环处理最为合理,这里以前写过两三遍了我们直接上代码:
#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("********* 0. exit **********\n");printf("*****************************************\n");
}void test()
{int input = 0;do{menu();printf("请选择操作数:> ");scanf("%d", &input);switch (input){case 1://addbreak;case 2://delbreak;case 3://searchbreak;case 4://modifybreak;case 5://showbreak;case 6://sortbreak;case 0:printf("退出成功!\n");break;default:printf("选择数非法!\n");break;}} while (input);
}int main()
{test();return 0;
}
看效果:
2、创建通讯录
我们在对通讯录进行各种操作前先得有个通讯录吧!通讯录的本质还是描述一个人的属性例如:姓名、年龄、性别、电话、住址等!这些都是不同的类型,我们很容易想到用结构体就能解决!但这只是描述一个人,你的通讯录不可能只有一个人吧应该是有很多个!而这些联系人又是同一类型,因此采用结构体数组来创建通讯录!
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[15];char addr[20];
}PeoInfo;
这就是描述一个人基本属性的结构体!当创建好了这个结构体后,我们就可以在test.c这个文件中do..while循环那里可以创建结构体数组来作为通讯录了!
但这样写会有个问题就是,当你存入或删除联系人后想打印出来看看是此时通讯录中的联系人时应该打印多少?我们还想不知道。所以这里我们还得再定义一个变量sz来记录通讯录的大小!
其实我们发现,sz不仅是可以记录通讯录的大小,而且增加联系人的时候只需要增加到sz位置即可,增加一个sz++,当删除一个sz--;这样其实 sz和通讯录是绑定在一起的,我们不妨在对通讯录进行一层封装,把他也封装成一个结构体,一个成员是PeoInfo结构体数组,一个是记录通讯录大小的sz:
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[15];char addr[20];
}PeoInfo;typedef struct Contact
{PeoInfo data[100];int sz;
}Contact;
然后再在test.c文件中创建即可!
OK!这个创建通讯录记好了!其实这里还可已在稍微优化一下:我们发现两个结构体中都出现了大量的常数,我们万一后期需求有变,这些都得改!因此为了后期修改方便,我们把这些都改成#define的常量,后期修改起来容易!这里你可能会说上期不是介绍了枚举吗,这里正好是多个常量枚举不是更好吗?这里枚举确实可以达到目的,但枚举是在没开始前就能列举出来所有可能,我们这个可不是!所以,这里不用枚举!
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 20
#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;
这样才是当前逻辑下的最优代码!
3、初始化通讯录
当我们创建好了通讯录后我们先对其进行一个初始化!把结构体数组的每个元素都初始化为0,通讯录大小也置为0!因为我们要改变结构体的内容所以应该传地址!我们这里采用的是memset这个函数直接初始化为0,也可以用个循环来初始化!不了解memset的点击memset
void InitContact(Contact* pc)
{assert(pc);memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}
4、添加联系人
我们前面已经说过了,只需要在sz的位置增加即可!但要考虑通讯录满的情况!
void AddContact(Contact* pc)
{assert(pc);//通讯录已满if (pc->sz == MAX){printf("通讯录已满,无法添加!\n");return;}//添加联系人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");
}
OK,我们下面先来写一个显示联系人,来验证一下是否添加成功!
5、显示联系人
显示联系人就是把通讯录中的联系人打印在控制台!
void ShowContact(Contact* pc)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");for (int i = 0; i < pc->sz; i++){printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}
OK,我们来看看是否添加成功以及显示联系人!
OK,添加联系人和显示联系人都成功了!说明我们上面逻辑没有问题!
6、删除指定联系人
我们输入要删除的人的姓名,进行查找,找到了删除,否则输出没有此联系人!注意考虑通讯录为空的情况!这里提到了查找,这个查找和通讯录的那个查找有点不一样,这个是查找后返回下标即可,那个是还要打印联系人信息功能不一样。因此我们单独在封装一个函数!
//查找要删除的人并返回下标
int FindContact(const Contact* pc, const char* name)
{for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//没找到
}//删除联系人
void DelContact(Contact* pc)
{assert(pc);//通讯录为空if (pc->sz == 0){printf("通讯录为空,无法删除!\n");return;}char name[NAME_MAX] = { 0 };printf("请输入要删除人的姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){//删除for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人!\n");}else{printf("没有此联系人!\n");}
}
这里要注意的是比较字符串要用strcmp,不能用 == 这个我们在字符串函数和内存函数那一期详细介绍过!另外注意删除的时候那里下标越界问题!
看效果:
7、查找指定联系人
查找指定联系人还是先按指定方式进行查找, 找到了打印,否则输出没有此联系人!这里显示一个联系人的这个里后面修改联系人也可能要用所以我们为了方便把他封装成一个函数后面直接调用即可!
//显示单个联系人信息
void Print(const Contact* pc, int pos)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}//查找指定联系人
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找联系人姓名:> ");scanf("%s", name);int pos = -1;//初始化为-1表示没有此联系人for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){pos = i;//找到了break;}}if (pos != -1){Print(pc, pos);}else{printf("没有此联系人!\n");}
}
其实还可以在简洁一点,上面已经实现了FindContact我们可以用一下:
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找联系人姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){Print(pc, pos);}else{printf("没有此联系人!\n");}
}
看效果:
8、修改联系人信息
修改联系人信息这块就有很多可能,比如只修改一项例如名字或性别等,也有可能修改多项例如名字、年龄等,也有可能得全部修改这几种情况。当我们要修改之前群殴我们能还是期望有一个菜单供我们选择,当修改完一项后我们可能还要继续修改。基于此我们采用和test.c文件中得do...while循环+菜单!
//修改联系人信息菜单
void menu1()
{system("cls");printf("*****************************************\n");printf("********* 1. name 2. age **********\n");printf("********* 3. sex 4. tele **********\n");printf("********* 5. addr 6. all **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}//修改名字
void ModName(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人姓名:> ");scanf("%s", pc->data[ret].name);printf("修改成功!\n");
}//修改年龄
void ModAge(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人年龄:> ");scanf("%d", &pc->data[ret].age);printf("修改成功!\n");
}//修改性别
void ModSex(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人性别:> ");scanf("%s", &pc->data[ret].sex);printf("修改成功!\n");
}//修改电话
void ModTele(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人电话:> ");scanf("%s", &pc->data[ret].tele);printf("修改成功!\n");
}//修改住址
void ModAddr(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人住址:> ");scanf("%s", &pc->data[ret].addr);printf("修改成功!\n");
}void ModAll(Contact* pc, int ret)
{assert(pc);printf("请输入联系人姓名:> ");scanf("%s", pc->data[ret].name);printf("请输入联系人年龄:> ");scanf("%d", &pc->data[ret].age);printf("请输入联系人性别:> ");scanf("%s", pc->data[ret].sex);printf("请输入联系人电话:> ");scanf("%s", pc->data[ret].tele);printf("请输入联系人住址:> ");scanf("%s", pc->data[ret].addr);printf("修改成功!\n");
}//修改联系人
void ModContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("通讯录为空,无法修改!\n");return;}char name[NAME_MAX] = { 0 };printf("请输入要修改系人姓名:> ");scanf("%s", name);//判断是否有该联系人int ret = FindContact(pc, name);//有该联系人if (ret != -1){int n = 0;do{menu1();printf("请选择修改内容:> ");scanf("%d", &n);//修改switch (n){case 1:ModName(pc,ret);break;case 2:ModAge(pc, ret);break;case 3:ModSex(pc, ret);break;case 4:ModTele(pc, ret);break;case 5:ModAddr(pc, ret);break;case 6:ModAll(pc, ret);break;case 0:printf("修改结束!\n");break;default:printf("选择数非法!\n");break;}} while (n);}else{printf("没有此联系人!\n");}
}
看效果:(小编做了清屏)
修改前:
修改后:
OK,还是比较成功的!我们再来进行对他实现一下排序!
9、排序联系人信息
我们上面已经实现了通讯录的增删查改的基本功能!我们想让他再有一个排序功能,比如按名字排序,或年龄排序!我们在指针进阶那块介绍过回调函数,这里我们用qsort进行对通讯录排序!我们期望还是和上面一样一开始有个菜单选择!
//排序菜单
void menu2()
{system("cls");printf("**********************************\n");printf("***** 1. name 2. age **********\n");printf("**********************************\n");
}//名字比较函数
int cmp_name(const void* str1, const void* str2)
{//return strcmp(((PeoInfo*)str1)->name, ((PeoInfo*)str2)->name);return strcmp((((Contact*)str1)->data)->name, (((Contact*)str2)->data)->name);
}//名字排序
void SortName(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
}//年龄比较函数
int cmp_age(const void* str1, const void* str2)
{//return ((PeoInfo*)str1)->age - ((PeoInfo*)str2)->age;return (((Contact*)str1)->data)->age - (((Contact*)str2)->data)->age;
}
//年龄排序
void SortAge(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_age);
}//排序联系人信息
void SortContact(Contact* pc)
{assert(pc);//判断为空if (pc->sz == 0){printf("通讯录为空,无法排序!\n");Sleep(3000);system("cls");return;}menu2();int n = 0;printf("请选择排序方式:> ");scanf("%d", &n);//排序switch (n){case 1:SortName(pc);system("cls");printf("排序成功!\n");break;case 2:SortAge(pc);system("cls");printf("排序成功!\n");break;default:printf("选择数非法!\n");Sleep(3000);system("cls");break;}
}
看效果:
排序前(年龄):
排序后(年龄):
排序前(名字):
排序后(名字):
OK,实现了排序!
三、全部源码
contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<windows.h>#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 20
#define MAX 100typedef struct PeoInfo
{char name[NAME_MAX];int age;char sex[SEX_MAX];char tele[TELE_MAX];char addr[ADDR_MAX];
}PeoInfo;typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;//初始化通讯录
void InitContact(Contact* pc);//添加联系人
void AddContact(Contact* pc);//显示联系人
void ShowContact(const Contact* pc);//删除联系人
void DelContact(Contact* pc);//查找指定联系人
void Search(const Contact* pc);//修改联系人
void ModContact(Contact* pc);//排序联系人信息
void SortContact(Contact* pc);
contact.c
#include"contact.h"//初始化通讯录
void InitContact(Contact* pc)
{assert(pc);memset(pc->data, 0, sizeof(pc->data));pc->sz = 0;
}//增加联系人
void AddContact(Contact* pc)
{assert(pc);//通讯录已满if (pc->sz == MAX){printf("通讯录已满,无法添加!\n");Sleep(2000);return;}//添加联系人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");Sleep(1000);
}//显示联系人
void ShowContact(const Contact* pc)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");for (int i = 0; i < pc->sz; i++){printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}//查找要删除的人并返回下标
int FindContact(const Contact* pc, const char* name)
{for (int i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;//找到了}}return -1;//没找到
}//删除联系人
void DelContact(Contact* pc)
{assert(pc);if (pc->sz == 0){printf("通讯录为空,无法删除!\n");Sleep(2000);return;}char name[NAME_MAX] = { 0 };printf("请输入要删除人的姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){//删除for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("成功删除联系人!\n");Sleep(1000);}else{printf("没有此联系人!\n");Sleep(2000);}
}//显示单个联系人信息
void Print(const Contact* pc, int pos)
{assert(pc);printf("%-s\t%-s\t%-5s\t%-15s\t%-20s\n", "姓名", "年龄", "性别", "电话", "住址");printf("%-s\t%-d\t%-5s\t%-15s\t%-20s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}//查找指定联系人
void Search(const Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };printf("请输入要查找联系人姓名:> ");scanf("%s", name);int pos = FindContact(pc, name);if (pos != -1){Print(pc, pos);}else{printf("没有此联系人!\n");Sleep(2000);system("cls");}
}//修改联系人信息菜单
void menu1()
{system("cls");printf("*****************************************\n");printf("********* 1. name 2. age **********\n");printf("********* 3. sex 4. tele **********\n");printf("********* 5. addr 6. all **********\n");printf("********* 0. exit **********\n");printf("*****************************************\n");
}//修改名字
void ModName(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人姓名:> ");scanf("%s", pc->data[ret].name);
}//修改年龄
void ModAge(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人年龄:> ");scanf("%d", &pc->data[ret].age);
}//修改性别
void ModSex(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人性别:> ");scanf("%s", &pc->data[ret].sex);
}//修改电话
void ModTele(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人电话:> ");scanf("%s", &pc->data[ret].tele);
}//修改住址
void ModAddr(Contact* pc, int ret)
{assert(pc);printf("请输入修改后联系人住址:> ");scanf("%s", &pc->data[ret].addr);
}void ModAll(Contact* pc, int ret)
{assert(pc);printf("请输入联系人姓名:> ");scanf("%s", pc->data[ret].name);printf("请输入联系人年龄:> ");scanf("%d", &pc->data[ret].age);printf("请输入联系人性别:> ");scanf("%s", pc->data[ret].sex);printf("请输入联系人电话:> ");scanf("%s", pc->data[ret].tele);printf("请输入联系人住址:> ");scanf("%s", pc->data[ret].addr);
}//修改联系人
void ModContact(Contact* pc)
{assert(pc);//判断是否为空if (pc->sz == 0){printf("通讯录为空,无法修改!\n");Sleep(2000);system("cls");return;}char name[NAME_MAX] = { 0 };printf("请输入要修改系人姓名:> ");scanf("%s", name);//判断是否有该联系人int ret = FindContact(pc, name);//有该联系人if (ret != -1){int n = 0;do{menu1();Print(pc, ret);printf("请选择修改内容:> ");scanf("%d", &n);//修改switch (n){case 1:ModName(pc,ret);break;case 2:ModAge(pc, ret);break;case 3:ModSex(pc, ret);break;case 4:ModTele(pc, ret);break;case 5:ModAddr(pc, ret);break;case 6:ModAll(pc, ret);break;case 0:printf("修改结束!\n");Sleep(2000);system("cls");break;default:printf("选择数非法!\n");Sleep(2000);system("cls");break;}} while (n);}else{printf("没有此联系人!\n");Sleep(2000);system("cls");}
}//排序菜单
void menu2()
{system("cls");printf("**********************************\n");printf("***** 1. name 2. age **********\n");printf("**********************************\n");
}//名字比较函数
int cmp_name(const void* str1, const void* str2)
{//return strcmp(((PeoInfo*)str1)->name, ((PeoInfo*)str2)->name);return strcmp((((Contact*)str1)->data)->name, (((Contact*)str2)->data)->name);
}//名字排序
void SortName(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
}//年龄比较函数
int cmp_age(const void* str1, const void* str2)
{//return ((PeoInfo*)str1)->age - ((PeoInfo*)str2)->age;return (((Contact*)str1)->data)->age - (((Contact*)str2)->data)->age;
}
//年龄排序
void SortAge(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_age);
}//排序联系人信息
void SortContact(Contact* pc)
{assert(pc);//判断为空if (pc->sz == 0){printf("通讯录为空,无法排序!\n");Sleep(3000);system("cls");return;}menu2();int n = 0;printf("请选择排序方式:> ");scanf("%d", &n);//排序switch (n){case 1:SortName(pc);system("cls");printf("排序成功!\n");break;case 2:SortAge(pc);system("cls");printf("排序成功!\n");break;default:printf("选择数非法!\n");Sleep(3000);system("cls");break;}
}
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("********* 0. exit **********\n");printf("*****************************************\n");
}void test()
{int input = 0;Contact con;InitContact(&con);do{menu();printf("请选择操作数:> ");scanf("%d", &input);switch (input){case 1:AddContact(&con);system("cls");break;case 2:DelContact(&con);system("cls"); break;case 3:system("cls");Search(&con);break;case 4:ModContact(&con);break;case 5:system("cls");ShowContact(&con);break;case 6:SortContact(&con);break;case 0:printf("退出成功!\n");break;default:printf("选择数非法!\n");break;}} while (input);
}int main()
{test();return 0;
}
OK,本期分享就到这里!好兄弟,我们下期再见!