一.了解项目功能
在本次实战项目中我们的目标是实现一个通讯录:
该通讯录可以用来存储1000个人的信息
每个人的信息包括:姓名、年龄、性别、住址、电话
通讯录提供功能有:
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 清空所有联系人
- 用四种排序方式对所有联系人进行排序
二.项目功能演示
要编写一个通讯录项目,首先要明确我们想要达到的效果是什么样,下面我将用vs2022编译器来为大家演示一下通讯录运行时的样子:
首先,我们来到菜单界面,提醒用户选择通讯录中的某一功能:
当用户选择'0'时,退出程序:
当用户选择'1'时,依次提醒用户输入增添的联系人信息,包括:名字,年龄,性别,地址,电话.
输入完毕后提示用户联系人添加成功:
当用户输入'2'时,提示用户输入要删除的人的名字,当用户输入后,提示删除成功:
当没有找到要删除的对象时,提示用户没有找到要删除的对象:
当用户输入'3'时, 提示用户输入要查找的联系人姓名,并在找到后打印出该联系人的信息:
如果没有找到,则提示用户没有找到该联系人:
当用户输入'4'时,提示用户输入要修改的人的名字,并在之后提示用户依次输入修改后的该联系人信息,录入完成后,提示修改成功并再次打印修改后的该联系人信息.
当用户输入联系人不存在时,提示用户没有找到要修改的对象:
当用户选择'5'时,打印当前通讯录中所有联系人的信息:
当用户选择'7'时, 提示用户选择排序方式.
当选择1时,则将通讯录按姓名排序并打印排序结果:
当选择2时,则将通讯录按年龄排序并打印排序结果:
当选择3时,则将通讯录按性别排序并打印排序结果:
当选择4时,则将通讯录按住址排序并打印排序结果:
当用户选择'6'时,清空通讯录中的所有联系人信息:
当通讯录原本为空时,提示用户无需清空:
以上就是通讯录的功能演示,下面我们则需要分12步,逐步实现上面演示的所有功能.
三.逐步实现项目功能及其逻辑详解
通过第二部分对项目功能的介绍,我们已经对通讯录的功能有了大致的了解,虽然看似需要实现的功能很多,貌似一时间不知该如何下手,但我们可以分步分模块来分析这个项目的流程,最后再将各各部分进行整合,所以大家不用担心,跟着我一步一步分析吧!
!!!注意,该部分的代码只是为了详细介绍某一部分的项目实现逻辑,故可能会删减一些与该部分不相关的代码以便大家理解,需要查看完整详细代码可以移步本文第四部分。
1.实现通讯录菜单
菜单部分的逻辑比较简单,就是利用C语言printf函数打印出这个菜单界面即可。基础问题就不过多赘述了,代码如下:
void menu() { printf("******************************************\n"); printf("******************************************\n"); printf("******* 1.add 2.del *******\n"); printf("******* 3.search 4.modify *******\n"); printf("******* 5.show 6.empty *******\n"); printf("******* 7.sort 0.exit *******\n"); printf("******************************************\n"); printf("******************************************\n"); }
2.实现通讯录程序功能可循环使用
由于我们要实现通讯录功能可以反复使用的逻辑,因此选择do...while的循环语句来实现这一部分的逻辑,该部分每步的详细解释见代码注释:
void menu()//菜单函数实现打印菜单 { printf("******************************************\n"); printf("******************************************\n"); printf("******* 1.add 2.del *******\n"); printf("******* 3.search 4.modify *******\n"); printf("******* 5.show 6.empty *******\n"); printf("******* 7.sort 0.exit *******\n"); printf("******************************************\n"); printf("******************************************\n"); } int main() { int input = 0; Contact con;//创建通讯录 InitContact(&con);//初始化通讯录 do//使用do while语句来实现通讯录功能可以反复使用 { menu();//菜单函数,打印菜单提示用户选择 printf("请选择:>");//提醒用户选择 scanf("%d", &input);//用scanf接收用户选择存入变量input中 switch (input)//利用分支语句实现用户的选择 { case 1://当用户选择'1',增添联系人 AddContact(&con); break; case 2://当用户选择'2',删除联系人 DelContact(&con); break; case 3://当用户选择'3',查找联系人 searchContact(&con); break; case 4://当用户选择'4',修改联系人 modifyContact(&con); break; case 5://当用户选择'5',打印联系人 ShowContact(&con); break; case 6://当用户选择'6',清空联系人 EmptyContact(&con); break; case 7://当用户选择'7',排序联系人 SortContact(&con); break; case 0://当用户输入0,提醒用户退出程序 printf("退出程序:>\n"); break; default://当用户输入了非选项数字时,提醒用户重新输入 printf("输入非法,请重新输入!\n"); break; } } while (input);//用变量input的值作为while循环的判定执行条件 //当input不为0时,该循环都可一直运行下去。 return 0; }
3.创建通讯录
创建通讯录成员的结构体应包括:姓名,年龄,性别,地址和电话这5个信息.
因此我们创建PeoInfo结构体类型时应由四个字符数组以及一个整形来组成.
同时我们需要一个整形来记录目前该通讯录中已存入的联系人个数.
因此我们创建Contact结构体类型时应由一个整形sz及一个结构体数组data组成.
而使用宏定义可以方便我们后续对各个结构的大小做调整.
综上,该部分代码如下:
#define MAX 1000 #define NAME_MAX 20 #define SEX_MAX 6 #define ADDR_MAX 30 #define TELE_MAX 12 //人的信息-结构体 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char addr[ADDR_MAX]; char tele[TELE_MAX];//电话11位,留一位给'\0'; }PeoInfo; typedef struct Contact { PeoInfo data[MAX];//存放1000个人的信息 int sz;//用来记录当前已经存放的信息的个数 }Contact;
4.初始化通讯录
初始化通讯录的逻辑非常简单,我们使用memset()函数完成对date结构体数组的空间初始化即可.
如果有对memset()函数不了解的朋友可以先移步我的这篇博客,在后续我们还会使用到这个函数:
该部分代码如下:
https://blog.csdn.net/weixin_72357342/article/details/131135673?spm=1001.2014.3001.5502
//初始化通讯录成员 void InitContact(Contact* pc) { assert(pc); pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); }
5.添加联系人信息
要添加联系人信息,首先需要判断当前通讯录是否还能添加,即判断联系人是否已经满了.
其次只需要逐一接收用户输入的信息存入对应结构体中即可:
该部分代码如下:
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].addr); printf("请输入电话;>"); scanf("%s", pc->data[pc->sz].tele); //每次录入完,sz自增 pc->sz++; printf("联系人添加成功:>\n"); }
6.按任人名查找指定联系人
因为后续删除,查找,修改等功能都是以输入姓名然后查找到对应联系人后进行操作的,
所以我们提前编写好一个用于人名查询的被调函数,
该被调函数的功能为:接收主调函数传入的人名信息,并在通讯录中查找,
如果找到了就返回该联系人在通讯录中的序号,如果没有找到则返回-1.
代码如下:
int FindByName(const Contact* pc, char* name) { assert(pc); int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i; } } return -1; }
7.删除指定联系人信息
要删除首先要判断当前通讯录联系人个数是否为0,为0则无法再进行删除操作.
其次提示用户输入要删除的联系人姓名,交给FindByName()函数查找,
如果找到了,则从该位置依次前移联系人,从而达到删除联系人的效果,图示如下:
如果没有找到用户输入的联系人信息则不执行,代码如下:
void DelContact(Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //防止通讯录删完了 if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); return; } //找到要删除的对象 printf("请输入要删除的人的名字:>"); scanf("%s", name); int del=FindByName(pc,name); if (-1== del) { printf("抱歉,没有找到您要删除的对象;<\n"); return; } int i = 0; //删除 for (i = del; i < pc->sz-1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功;>\n"); }
8.查找联系人信息并打印
该部分实现较为简单,只需要借助FindByName函数找到联系人位置并打印其信息即可,
代码如下:
void searchContact(const Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //找到要查找的对象 printf("请输入要查找的人的名字:>"); scanf("%s", name); int sea = FindByName(pc, name); if (-1 == sea) { printf("抱歉,没有找到您要查找的对象;<\n"); return; } //打印信息 printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n",sea+1, pc->data[sea].name, pc->data[sea].age, pc->data[sea].sex, pc->data[sea].addr, pc->data[sea].tele); printf("________________________________________________________________________________________\n"); }
9.修改指定联系人信息
该部分功能实现较为简单,只需要借助FindByName函数找到联系人位置并在重新录入其信息后打印修改后的信息即可,
代码如下:
void modifyContact(Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //找到要修改的对象 printf("请输入要修改的人的名字:>"); scanf("%s", name); int mod = FindByName(pc, name); if (-1 == mod) { printf("抱歉,没有找到您要修改的对象;<\n"); return; } printf("请输入修改后的联系人信息\n"); printf("请输入名字;>"); scanf("%s", pc->data[mod].name); printf("请输入年龄;>"); scanf("%d", &(pc->data[mod].age)); printf("请输入性别;>"); scanf("%s", pc->data[mod].sex); printf("请输入地址;>"); scanf("%s", pc->data[mod].addr); printf("请输入电话;>"); scanf("%s", pc->data[mod].tele); printf("修改成功;>\n"); printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n", mod + 1, pc->data[mod].name, pc->data[mod].age, pc->data[mod].sex, pc->data[mod].addr, pc->data[mod].tele); printf("________________________________________________________________________________________\n"); }
10.显示所有联系人信息
该部分只需要借助循环结构将通讯录中所有成员打印即可,但需要使用大量格式控制符来确保打印的通讯录整洁,美观.
代码如下:
void ShowContact(const Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无法显示\n"); return; } int i = 0; printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号","|姓名", "|年龄", "|性别", "|地址", "|电话"); for (i = 0; i < pc->sz; i++) { printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n",i+1, pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele); } printf("________________________________________________________________________________________\n"); }
11.清空所有联系人
清空所有联系人的逻辑与最开始的初始化通讯录的逻辑相同,借助memset()函数完成即可,
但在清空前需要判断当前通讯录是否为空,如果为空则不需要进行清空操作,
代码如下:
void EmptyContact(Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无需清空\n"); return; } pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); printf("清空成功:>\n"); }
12.用四种排序方式给所有联系人排序
该部分分为两个模块:
- 排序逻辑的实现
- 借助qsort函数排序,需要完成排序函数comper的编写.
在实现第一模块时,尤其要注意使用qsort()函数时参数的传递,如果有对qsort()函数还不了解的朋友可以移步我的这篇博客:qsort()函数详解:能给万物排序的神奇函数
里面有0基础的qsort()函数(快速排序函数)的详解,应该可以帮助你完全掌握该函数.
下面是第一模块的代码:
//给联系人排序 void SortContact(Contact* pc) { int cho = 0; if (pc->sz == 0) { printf("通讯录为空,请先加入联系人\n"); return; } printf("******************************\n"); printf("******** 1.按姓名 *********\n"); printf("******** 2.按年龄 *********\n"); printf("******** 3.按性别 *********\n"); printf("******** 4.按地址 *********\n"); printf("******************************\n"); printf("请选择您想使用的排序方式:>"); scanf("%d", &cho); int (*compare_func)(const void*, const void*) = NULL; switch (cho) { case 1: compare_func = comper_name; break; case 2: compare_func = comper_age; break; case 3: compare_func = comper_sex; break; case 4: compare_func = comper_addr; break; default: printf("排序选项无效:<\n"); return; } // 使用qsort函数进行排序 qsort(pc->data, pc->sz, sizeof(PeoInfo), compare_func); printf("排序成功:>\n"); ShowContact(pc); }
下面是第二模块的代码:
//comper按姓名排序 int comper_name(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->name, personB->name); } //comper按年龄排序 int comper_age(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return (personA->age-personB->age); } //comper按性别排序 int comper_sex(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->sex, personB->sex); } //comper按住处排序 int comper_addr(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->addr, personB->addr); }
四.整合代码并测试运行
我们将程序运行的代码分为三个模块分开书写,完整代码如下:
contact.c
//contact.c通讯录的实现 #include"contact.h" //初始化通讯录成员 void InitContact(Contact* pc) { assert(pc); pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); } //增加通讯录中的联系人 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].addr); printf("请输入电话;>"); scanf("%s", pc->data[pc->sz].tele); //每次录入完,sz自增 pc->sz++; printf("联系人添加成功:>\n"); } //按人名查找联系人 int FindByName(const Contact* pc, char* name) { assert(pc); int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i; } } return -1; } //删除通讯录中的成员 void DelContact(Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //防止通讯录删完了 if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); return; } //找到要删除的对象 printf("请输入要删除的人的名字:>"); scanf("%s", name); int del=FindByName(pc,name); if (-1== del) { printf("抱歉,没有找到您要删除的对象;<\n"); return; } int i = 0; //删除 for (i = del; i < pc->sz-1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功;>\n"); } //查找联系人信息并打印 void searchContact(const Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //找到要查找的对象 printf("请输入要查找的人的名字:>"); scanf("%s", name); int sea = FindByName(pc, name); if (-1 == sea) { printf("抱歉,没有找到您要查找的对象;<\n"); return; } //打印信息 printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n",sea+1, pc->data[sea].name, pc->data[sea].age, pc->data[sea].sex, pc->data[sea].addr, pc->data[sea].tele); printf("________________________________________________________________________________________\n"); } //修改联系人信息 void modifyContact(Contact* pc) { assert(pc); char name[NAME_MAX] = { 0 }; //找到要修改的对象 printf("请输入要修改的人的名字:>"); scanf("%s", name); int mod = FindByName(pc, name); if (-1 == mod) { printf("抱歉,没有找到您要修改的对象;<\n"); return; } printf("请输入修改后的联系人信息\n"); printf("请输入名字;>"); scanf("%s", pc->data[mod].name); printf("请输入年龄;>"); scanf("%d", &(pc->data[mod].age)); printf("请输入性别;>"); scanf("%s", pc->data[mod].sex); printf("请输入地址;>"); scanf("%s", pc->data[mod].addr); printf("请输入电话;>"); scanf("%s", pc->data[mod].tele); printf("修改成功;>\n"); printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号", "|姓名", "|年龄", "|性别", "|地址", "|电话"); printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n", mod + 1, pc->data[mod].name, pc->data[mod].age, pc->data[mod].sex, pc->data[mod].addr, pc->data[mod].tele); printf("________________________________________________________________________________________\n"); } //显示通讯录中的信息 void ShowContact(const Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无法显示\n"); return; } int i = 0; printf("________________________________________________________________________________________\n"); printf("%-4s\t%-20s\t%-4s\t%-6s\t%-20s\t%-12s\n", "|序号","|姓名", "|年龄", "|性别", "|地址", "|电话"); for (i = 0; i < pc->sz; i++) { printf("________________________________________________________________________________________\n"); printf("|%-4d\t|%-20s\t|%-4d\t|%-6s\t|%-20s\t|%-12s\n",i+1, pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].addr, pc->data[i].tele); } printf("________________________________________________________________________________________\n"); } //清空联系人信息 void EmptyContact(Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录为空,无需清空\n"); return; } pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); printf("清空成功:>\n"); } //comper按姓名排序 int comper_name(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->name, personB->name); } //comper按年龄排序 int comper_age(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return (personA->age-personB->age); } //comper按性别排序 int comper_sex(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->sex, personB->sex); } //comper按住处排序 int comper_addr(const void* a, const void* b) { const PeoInfo* personA = (const PeoInfo*)a; const PeoInfo* personB = (const PeoInfo*)b; return strcmp(personA->addr, personB->addr); } //给联系人排序 void SortContact(Contact* pc) { int cho = 0; if (pc->sz == 0) { printf("通讯录为空,请先加入联系人\n"); return; } printf("******************************\n"); printf("******** 1.按姓名 *********\n"); printf("******** 2.按年龄 *********\n"); printf("******** 3.按性别 *********\n"); printf("******** 4.按地址 *********\n"); printf("******************************\n"); printf("请选择您想使用的排序方式:>"); scanf("%d", &cho); int (*compare_func)(const void*, const void*) = NULL; switch (cho) { case 1: compare_func = comper_name; break; case 2: compare_func = comper_age; break; case 3: compare_func = comper_sex; break; case 4: compare_func = comper_addr; break; default: printf("排序选项无效:<\n"); return; } // 使用qsort函数进行排序 qsort(pc->data, pc->sz, sizeof(PeoInfo), compare_func); printf("排序成功:>\n"); ShowContact(pc); }
contact.h
#pragma once //contact.h函数的声明 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #define MAX 1000 #define NAME_MAX 20 #define SEX_MAX 6 #define ADDR_MAX 30 #define TELE_MAX 12 //人的信息-结构体 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char addr[ADDR_MAX]; char tele[TELE_MAX];//电话11位,留一位给'\0'; }PeoInfo; typedef struct Contact { PeoInfo data[MAX];//存放1000个人的信息 int sz;//用来记录当前已经存放的信息的个数 }Contact; //初始化通讯录 void InitContact(Contact* pc); //增加联系人 void AddContact(Contact* pc); //删除通讯录中的信息 void DelContact(Contact* pc); //显示通讯录中的信息 void ShowContact(const Contact* pc); //查找联系人并打印其信息 void searchContact(const Contact* pc); //修改联系人信息 void modifyContact(Contact* pc); //清空联系人信息(和初始化逻辑一模一样) void EmptyContact(Contact* pc); //给联系人排序 void SortContact(Contact* pc);
test.c
//bit.c测试通讯录 #include"contact.h" void menu() { printf("******************************************\n"); printf("******************************************\n"); printf("******* 1.add 2.del *******\n"); printf("******* 3.search 4.modify *******\n"); printf("******* 5.show 6.empty *******\n"); printf("******* 7.sort 0.exit *******\n"); printf("******************************************\n"); printf("******************************************\n"); } int main() { int input = 0; //创建通讯录 Contact con; //初始化通讯录 InitContact(&con); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: AddContact(&con); break; case 2: DelContact(&con); break; case 3: searchContact(&con); break; case 4: modifyContact(&con); break; case 5: ShowContact(&con); break; case 6: EmptyContact(&con); break; case 7: SortContact(&con); break; case 0: printf("退出程序:>\n"); break; default: printf("输入非法,请重新输入!\n"); break; } } while (input); return 0; }
测试运行部分则在开篇项目功能演示部分就已经测试运行过了,有兴趣的朋友可以将代码拷贝在自己的编译器中运行完善,篇幅有限,我就不多赘述了.
希望这篇通讯录的实现详解能对大家有所帮助,欢迎大佬们留言或私信与我交流.
有关更多动态开辟相关知识可以移步:
https://blog.csdn.net/weixin_72357342/article/details/134099965?spm=1001.2014.3001.5502
学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!