【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.pswp.cn/bicheng/90035.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

缓存三剑客解决方案

缓存三剑客解决方案 1.缓存雪崩 定义&#xff1a; 大量缓存数据在同一时间点集体失效&#xff0c;导致所有请求直接穿透到数据库&#xff0c;引发数据库瞬时高负载甚至崩溃。 解决方案&#xff1a; 设置过期随机值&#xff0c;避免大量缓存同时失效。 // 缓存雪崩防护&#xff…

HTML 页面禁止缩放功能

页面禁止缩放 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1, shrink-to-fitno, maximum-scale1.0, us…

在github上搭建自己主页

主要是这篇博客进行一些补充。 第一步照做就行 首先是第二步 克隆仓库到本地 先下载一个git&#xff0c;电脑创建一个新文件夹&#xff0c;然后 git clone xxxxx 注意即使你使用了代理&#xff0c;这里大概率也会报错&#xff0c;Failed to connect to github.com port 443 …

Laravel 框架NOAUTH Authentication required 错误解决方案-优雅草卓伊凡

Laravel 框架NOAUTH Authentication required 错误解决方案-优雅草卓伊凡NOAUTH Authentication required 错误这个错误通常出现在以下几种情况&#xff1a;Redis 认证问题&#xff1a;如果你的应用使用了 Redis 且配置了密码API 认证问题&#xff1a;请求需要认证的 API 端点但…

kafka生产端和消费端的僵尸实例以及解决办法

目录 一 生产端僵尸 1.1 原因 1.2 问题 1.3解决办法 1.4 案例 1.4.1 案例1&#xff1a;生产者崩溃后重启 (同一 transactional.id) 1.4.2 案例2&#xff1a;短暂网络分区导致的脑裂 1.4.3 案例3&#xff1a;正确 - 解决僵尸 1.4.4 案例4&#xff1a;错误 - 无法解决僵…

国产电科金仓数据库金仓KES V9 2025:AI时代的数据库融合标杆

国产电科金仓数据库金仓KES V9 2025&#xff1a;AI时代的数据库融合标杆 在AI技术迅猛发展的今天&#xff0c;企业数据管理面临着前所未有的挑战&#xff1a;异构数据库兼容难题、多数据模型融合需求、高并发场景性能瓶颈、跨中心容灾压力……这些痛点如同数据流转的大问题&am…

【STM32】关于STM32F407写Flash失败问题的解决办法

问题描述 在使用正点原子的STM32F407写flash例程时&#xff0c;发现STMFLASH_Write函数没办法写入数据到flash&#xff0c;原始代码输入下&#xff1a; 随后对每一行代码的结果进行分析&#xff0c;发现87行的“FLASH_ProgramWord(WriteAddr,*pBuffer)”返回值是7&#xff0c;一…

CUDA与RISC-V的融合:打破架构霸权,重塑AI计算未来

当x86和Arm统治数据中心十余年后,一家GPU巨头正悄悄将十亿颗RISC-V核心嵌入其系统。如今,它决定拆除CPU架构的围墙。 2025年7月,上海张江科学会堂。英伟达硬件工程副总裁Frans Sijstermanns在第五届RISC-V中国峰会上宣布:英伟达正式启动CUDA向RISC-V架构的移植工作。 这个…

微信二维码扫描登录流程详解

二维码扫描登录流程细节&#xff08;项目经验&#xff09; 1&#xff1a; 获取二维码信息 PC会优先存放服务器生成的唯一密钥&#xff1a; 比如 source、secret 以密文形式存储大致发送字段&#xff1a; sourcesecretmac(mac 地址) 服务器生成 二维码信息&#xff1a;二维码字符…

日本上市IT企业|8月125日将在大连举办赴日it招聘会

株式会社GSD的核心战略伙伴贝斯株式会社&#xff0c;将于2025年8月25日在大连香格里拉大酒店商务会议室隆重举办赴日技术人才专场招聘会。本次招聘会面向全国范围内的优秀IT人才&#xff0c;旨在为贝斯株式会社东京本社长期发展招募优质的系统开发与管理人才。招聘计划&#xf…

Python 数据分析与可视化:从基础到进阶的技术实现与优化策略

数据分析与可视化是数据科学领域的核心技能,Python 凭借其丰富的库生态和灵活的编程范式,成为该领域的首选工具。本文将系统讲解 Python 数据分析与可视化的技术栈实现,从基础操作到性能优化,结合实战场景提供可复用的解决方案。 数据分析核心库技术解析 Pandas 数据处理…

Rust Web 全栈开发(十):编写服务器端 Web 应用

Rust Web 全栈开发&#xff08;十&#xff09;&#xff1a;编写服务器端 Web 应用Rust Web 全栈开发&#xff08;十&#xff09;&#xff1a;编写服务器端 Web 应用创建成员库&#xff1a;webappmodelshandlersrouterserrorsmodsvrstaticteachers.htmlregister.htmlbootstrap.m…

每日面试题11:JVM

深入理解JVM&#xff1a;Java的“心脏”如何驱动程序运行&#xff1f;为什么需要JVM&#xff1f;你是否想过&#xff0c;为什么用Java写的程序&#xff0c;能在Windows、Linux、macOS上“无缝运行”&#xff1f;为什么开发者无需为不同操作系统重写代码&#xff1f;这背后的核心…

Linux网络信息(含ssh服务和rsync)

73.telnet&#xff1a;测试端口连通性用法&#xff1a;telnet 主机名或IP 端口号测试目标主机的指定端口是否开放&#xff0c;检查网络服务连通性。eg&#xff1a;telnet www.baidu.com 80# 说明&#xff1a;# - 如果连接成功&#xff0c;显示 "Connected to ..."。…

【PTA数据结构 | C语言版】我爱背单词

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 作为一个勤奋的学生&#xff0c;你在阅读一段英文文章时&#xff0c;是否希望有个程序能自动帮你把没有背过的生词列出来&#xff1f;本题就请你实现这个程序。 输入格式&#xff1a; 输入第 1 行给…

如何使用电脑连接小米耳机(红米 redmi耳机)

如何使用电脑连接小米&#xff08;红米 redmi&#xff09;耳机Redmi耳机连接电脑的具体步骤如下注意事项和常见问题解决方法&#xff1a;Redmi耳机连接电脑的具体步骤如下 打开耳机仓盖&#xff1a; 首先&#xff0c;打开Redmi耳机的充电仓盖&#xff0c;但不需要取出耳机。进…

排序算法—交换排序(冒泡、快速)(动图演示)

目录 十大排序算法分类​编辑 冒泡排序 算法步骤&#xff1a; 动图演示&#xff1a; 性能分析&#xff1a; 代码实现&#xff08;Java&#xff09;&#xff1a; 快速排序&#xff08;挖坑法&#xff09; 算法步骤&#xff1a; 动图演示&#xff1a; 性能分析&#xff1…

2023 年 5 月青少年软编等考 C 语言八级真题解析

目录 T1. 道路 思路分析 T2. Rainbow 的商店 思路分析 T3. 冰阔落 I 思路分析 T4. 青蛙的约会 思路分析 T1. 道路 题目链接:SOJ D1216 N N N 个以 1 ∼ N 1 \sim N 1∼N 标号的城市通过单向的道路相连,每条道路包含两个参数:道路的长度和需要为该路付的通行费(以金币的数…

【vue-4】深入理解 Vue 3 中的 v-for 指令

Vue.js 作为现代前端框架的代表之一&#xff0c;其模板指令系统提供了强大的数据绑定和渲染能力。其中&#xff0c;v-for 指令是 Vue 中最常用且最重要的指令之一&#xff0c;它允许我们基于数据源循环渲染元素或组件。在 Vue 3 中&#xff0c;v-for 保留了一贯的简洁语法&…

《R for Data Science (2e)》免费中文翻译 (第1章) --- Data visualization(1)

写在前面 本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github&#xff0c;欢迎大家参与贡献&#xff0c;详细信息见&#xff1a; Books-zh-cn 项目介绍&#xff1a; Books-zh-cn&#xff1a;开源免费的中文书籍社区 r4ds-zh-cn …