当前位置: 首页 > news >正文

【C语言进阶】结构体练习:通讯录

要求:

实现一个通讯录。

(1)人的信息:

        包括姓名、年龄、性别、电话地址。

(2)功能:

        ①存放一百个人的信息。

        ②增加联系人。

        ③删除指定联系人。

        ④查找指定联系人。

        ⑤修改联系人。

        ⑥排序。

        ⑦显示联系人。

(3)文件:

        ①contact.c:通讯录实现的文件。

        ②contact.h:通讯录声明的文件。

        ③test.c      :测试通讯录的文件。

目录

1.框架的搭建

2. 结构体的声明以及初始化

2.1 结构体的声明

2.2 结构体的初始化

3. 通讯录的增加功能

4. 展示通讯录的功能

5.删除、查找指定联系人

6.查找联系人

7. 修改联系人

8.排序联系人

9.完整代码

9.1 contact.h

9.2 contact.c

9.3 test.c

10.功能演示

10.1添加、展示

10.2 删除

10.3 查找

10.4 修改

10.5 排序


1.框架的搭建

         整体采用dowhile的结构,读取用户的输入根据不同的输入选项,调用不同的方法从而实现不同的功能。

在test.c中代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>void menu()
{printf("=====================================");printf("======1.add			2.del	 ========");printf("======3.search		4.modify ========");printf("======5.show		6.sort	 ========");printf("======0.exit		         ========");printf("=====================================");
}int main()
{int input = 0;do{menu();printf("请输入一个选项:\n");scanf("%d", &input);switch (input){case 1:break;case 2:break;case 3:break;case 4:break;case 5:break;case 6:break;case 0:printf("程序已退出!\n");break;default:printf("请正确输入选项!\n");break;}} while (input);return 0;
}

2. 结构体的声明以及初始化

2.1 结构体的声明

        这个部分需要在头文件中contact.h,编写完毕之后若要使用改结构体需要在test.c中引用该头文件。

        首先声明一个人信息的结构体,具体成员变量详见文章开头。

        我们思考一下,如果我们在主函数中创建了一个100个人信息的结构体数组,我们还需要一个变量来记录这100个变量中实际存放的变量的个数,那么我们可以封装成一个通讯录结构体,通讯录结构体中包含一个人信息结构体数组和实际存放结构体变量的数目。

        此时可以在主函数声明一个通讯录结构体变量,这样就会更加的方便。

2.2 结构体的初始化

        需要将通讯录结构体变量进行初始化,我们可以在头文件声明一个初始化函数形参传入指针;将成员变量count置为0,将成员变量PeoInfo 类型的数组全部置为0,可以使用memset来实现。

        这里需要注意一个细节,既然测试函数和逻辑函数都引用头文件,那么不妨直接把第三方库全部放在头文件中,这样一来只需要引用头文件即可。

3. 通讯录的增加功能

①在头文件中进行方法的声明,形参需要传入通讯录变量的地址;

②具体实现在contact.c中实现。

③首先需要判断指针是否为空。

④如果通讯录的count == 100,说明通讯录已经满了,提示用户并且直接返回。

这里需要注意的是,在代码中出现的数字都可以使用define关键字进行宏定义,后面方便修改。

⑤count可以当做结构体数组下标一用,使用scanf来输入数据,这里需要注意的是数组名本就是地址,除了age需要取地址,剩余四个成员变量不需要取地址。

// 通讯录的增加方法
void addContact(Contact* p)
{assert(p); // 断言空指针if (p->count == N){printf("通讯录已满!存不下了!\n");return;}printf("请输入姓名:\n");scanf("%s",(p->list)[p->count].name);printf("请输入年龄:\n");scanf("%d", &((p->list)[p->count].age));printf("请输入性别:\n");scanf("%s", (p->list)[p->count].gender);printf("请输入电话号码:\n");scanf("%s", (p->list)[p->count].tele);printf("请输入地址:\n");scanf("%s", (p->list)[p->count].addr);(p->count)++;printf("通讯录增加成功!");
}

4. 展示通讯录的功能

①首先在头文件定义方法,这个方法的形参是一个const Contact的指针,因为只需要展示无需修改。

②遍历打印p指针指向的结构体即可。

③唯一需要注意的是:输出的格式要对齐,使用左对齐,在%后加上一个-号。

// 通讯录的显示
void showContact(const Contact* p)
{assert(p);int i = 0;printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n","姓名","年龄","性别","电话","地址");for(i = 0;i < p->count; i++){printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",(p->list)[i].name,(p->list)[i].age,(p->list)[i].gender,(p->list)[i].tele,(p->list)[i].addr);}
}

左对齐的样式:

5.删除、查找指定联系人

①首先需要判断Contact数组有效元素是否为空,如果为空直接返回;

②删除之前需要读取联系人姓名,根据姓名来查找,找到了就返回下标,没找到就返回-1;

③查找这个动作在修改、查找等功能都会用到,所以可以先封装成一个函数,具有两个参数,第一个参数是结构体的指针,第二个是查找的名字。

        (1)查找的逻辑:

                a.遍历通讯录中的PeoInfo数组;

                b.判断数组中的姓名是否和传入姓名一致;

                c.一致就返回下标,没找到就返回-1;

static int findByname(const Contact* p,char name[])
{assert(p);int i = 0;for ( i = 0; i < p->count; i++){if (strcmp(p->list[i].name,name ) == 0){return i;}}return -1;
}

查找函数不在外界暴露,所以不需要声明,最好加上关键字static修饰,只有本文件能够访问。 

④查找到了之后需要根据下标进行删除,其实就是将数组的此下标的后面元素向前覆盖即可;需要将有效数据count再-1;

// 通讯录的删除
void deleContact(Contact* p)
{char name[NAME_LONG] = { 0 };assert(p);// 判断通讯录联系人数量为0if (p->count == 0){printf("通讯录为空,无法删除\n");}printf("请输入要删除的联系人姓名\n");scanf("%s", name);int ret = findByname(p, name);if (ret == -1){printf("删除的联系人不存在!\n");return;}else{// 删除for (int i = ret; i < p->count - 1; i++){p->list[i] = p->list[i + 1];}p->count--; // 有效的数据-1printf("删除成功!!\n");}
}

6.查找联系人

        我们之前已经写过函数了,所以这里只需要封装一层。

// 通讯录的查找
void SearchContact(Contact* p) 
{char name[NAME_LONG];printf("请输入查找的姓名:\n");scanf("%s",name);int ret = findByname(p, name);if (ret == -1) {printf("该姓名不存在!\n");return;}else {printf("查找成功!以下是该联系人的信息:\n");printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",(p->list)[ret].name,(p->list)[ret].age,(p->list)[ret].gender,(p->list)[ret].tele,(p->list)[ret].addr);}
}

7. 修改联系人

①先根据姓名查找。

②找到了就根据下标来进行修改(和添加联系人雷同);

③找不到就提示并返回。

// 通讯录的修改
void ModifyContact(Contact* p) 
{char name[NAME_LONG];printf("请输入要修改的联系人姓名:\n");scanf("%s", name);int ret = findByname(p, name);if (ret == -1){printf("该姓名不存在!\n");return;}else{printf("请输入修改姓名:\n");scanf("%s", (p->list)[ret].name);printf("请输入修改年龄:\n");scanf("%d", &((p->list)[ret].age));printf("请输入修改性别:\n");scanf("%s", (p->list)[ret].gender);printf("请输入修改电话号码:\n");scanf("%s", (p->list)[ret].tele);printf("请输入修改地址:\n");scanf("%s", (p->list)[ret].addr);printf("通讯录修改成功!\n");}
}

8.排序联系人

①直接使用qsort函数即可,唯一需要注意的是明确按照什么来排序,这里按照姓名来排序

②首先保证传入的指针有效。

③qsort的第一个参数是排序的起始地址,第二个参数是排序元素的个数,第三个参数是每一个元素的字节数,第四个是一个比较函数,这个比较函数的形参有两个void*指针,需要强转成排序的两个元素的指针类型。

size_t contact_cmp(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}// 通讯录的排序
void SortContact(Contact* p)
{assert(p);qsort(p->list, p->count, sizeof(p->list[0]), contact_cmp);printf("排序成功!\n");
}

9.完整代码

9.1 contact.h

#define _CRT_SECURE_NO_WARNINGS
#define N 100
#define NAME_LONG 20
#define GENDER_LONG 10
#define TELE_LONG 12
#define ADDRESS_LONG 30
#include<string.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
// 人信息的结构体声明
typedef struct PeoInfo
{char name[NAME_LONG]; // 姓名int age; // 年龄char gender[GENDER_LONG]; // 性别char tele[TELE_LONG]; // 电话号码char addr[ADDRESS_LONG]; // 地址
}PeoInfo;// 通讯录结构体的声明
typedef struct Contact 
{// 通讯录假如可以存放一百条信息PeoInfo list[N];// 真实存放的信息的条数int count;
}Contact;// 初始化通讯录结构体变量
void InitContact(Contact* p);// 通讯录的增加方法
void addContact(Contact* p);// 通讯录的显示
void showContact(const Contact* p);// 通讯录的删除
void deleContact(Contact* p);// 通讯录的查找
void SearchContact(Contact* p);// 通讯录的修改
void ModifyContact(Contact* p);// 通讯录的排序
void SortContact(Contact* p);

9.2 contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"// 初始化通讯录结构体变量
void InitContact(Contact* p)
{assert(p);p->count = 0;memset(p, 0, sizeof(p->list)); // list是PeoInfo的结构体数组
}// 通讯录的增加方法
void addContact(Contact* p)
{assert(p);if (p->count == N){printf("通讯录已满!存不下了!\n");return;}printf("请输入姓名:\n");scanf("%s", (p->list)[p->count].name);printf("请输入年龄:\n");scanf("%d", &((p->list)[p->count].age));printf("请输入性别:\n");scanf("%s", (p->list)[p->count].gender);printf("请输入电话号码:\n");scanf("%s", (p->list)[p->count].tele);printf("请输入地址:\n");scanf("%s", (p->list)[p->count].addr);(p->count)++;printf("通讯录增加成功!\n");
}// 通讯录的显示
void showContact(const Contact* p)
{assert(p);int i = 0;printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < p->count; i++){printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",(p->list)[i].name,(p->list)[i].age,(p->list)[i].gender,(p->list)[i].tele,(p->list)[i].addr);}
}static int findByname(const Contact* p, char name[])
{assert(p);int i = 0;for (i = 0; i < p->count; i++){if (strcmp(p->list[i].name, name) == 0){return i;}}return -1;
}// 通讯录的删除
void deleContact(Contact* p)
{char name[NAME_LONG] = { 0 };assert(p);// 判断通讯录联系人数量为0if (p->count == 0){printf("通讯录为空,无法删除\n");}printf("请输入要删除的联系人姓名\n");scanf("%s", name);int ret = findByname(p, name);if (ret == -1){printf("删除的联系人不存在!\n");return;}else{// 删除for (int i = ret; i < p->count - 1; i++){p->list[i] = p->list[i + 1];}p->count--; // 有效的数据-1printf("删除成功!!\n");}
}// 通讯录的查找
void SearchContact(Contact* p)
{char name[NAME_LONG];printf("请输入查找的姓名:\n");scanf("%s", name);int ret = findByname(p, name);if (ret == -1){printf("该姓名不存在!\n");return;}else{printf("查找成功!以下是该联系人的信息:\n");printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n",(p->list)[ret].name,(p->list)[ret].age,(p->list)[ret].gender,(p->list)[ret].tele,(p->list)[ret].addr);}
}// 通讯录的修改
void ModifyContact(Contact* p)
{char name[NAME_LONG];printf("请输入要修改的联系人姓名:\n");scanf("%s", name);int ret = findByname(p, name);if (ret == -1){printf("该姓名不存在!\n");return;}else{printf("请输入修改姓名:\n");scanf("%s", (p->list)[ret].name);printf("请输入修改年龄:\n");scanf("%d", &((p->list)[ret].age));printf("请输入修改性别:\n");scanf("%s", (p->list)[ret].gender);printf("请输入修改电话号码:\n");scanf("%s", (p->list)[ret].tele);printf("请输入修改地址:\n");scanf("%s", (p->list)[ret].addr);printf("通讯录修改成功!\n");}
}size_t contact_cmp(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}// 通讯录的排序
void SortContact(Contact* p)
{assert(p);qsort(p->list, p->count, sizeof(p->list[0]), contact_cmp);printf("排序成功!\n");
}

9.3 test.c

#define _CRT_SECURE_NO_WARNINGS
#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");
}int main()
{// 声明通讯录结构体变量Contact con;// 初始化通讯录结构体变量InitContact(&con);int input = 0;do{menu();printf("请输入一个选项:\n");scanf("%d", &input);switch (input){case 1:addContact(&con);break;case 2:deleContact(&con);break;case 3:SearchContact(&con);break;case 4:ModifyContact(&con);break;case 5:showContact(&con);break;case 6:SortContact(&con);break;case 0:printf("程序已退出!\n");break;default:printf("请正确输入选项!\n");break;}} while (input);return 0;
}

10.功能演示

10.1添加、展示

10.2 删除

10.3 查找

10.4 修改

10.5 排序

http://www.xdnf.cn/news/1163917.html

相关文章:

  • Day07_网络编程20250721_大项目
  • 从 “能用“ 到 “好用“:中小制造企业数字化转型中的 IT 系统优化管理策略
  • 高性能I/O的终极武器:epoll深度解析与实战
  • 什么是GNN?——聚合、更新与循环
  • 注册表清理优化丨Wise RegistryCleaner_v11.1.10.725(官方赠品)
  • USRP采集信号转换为时频图数据集
  • 理解向量及其运算-AI云计算数值分析和代码验证
  • Mac上安装Homebrew的详细步骤
  • CCLink IE转ModbusTCP网关与三菱PLC通讯无纸记录器
  • selenium爬取图书信息
  • 旋转目标检测(Rotated Object Detection)技术概述
  • Selenium 处理表单、弹窗与文件上传:从基础到实战
  • ACE 插入元件
  • cs336 Lecture2
  • 使用Langchain调用模型上下文协议 (MCP)服务
  • AI革命带来的便利
  • Go语言进阶书籍:Go语言高级编程(第2版)
  • 14.7 Alpaca格式深度解析:3倍指令准确率提升的LLM微调秘诀
  • Jenkins 不同节点间文件传递:跨 Job 与 同 Job 的实现方法
  • Linux | C Shell 与 Bash 的差异 / 环境变量配置问题解析
  • 了解 ReAct 框架:语言模型中推理与行动的协同
  • vscode 使用说明二
  • vscode创建vue项目报错
  • 5.6 framebuffer驱动
  • 人工智能之数学基础:事件间的关系
  • MySQL 核心知识点梳理(3)
  • Qualcomm Linux 蓝牙指南学习--验证 Fluoride 协议栈的功能(2)
  • Java学习----NIO模型
  • 爬虫实战指南:从定位数据到解析请求的全流程解析
  • PyTorch 实现 CIFAR-10 图像分类:从数据预处理到模型训练与评估