【数据结构项目】通讯录

在这里插入图片描述

个人主页点这里~

原文件在gitee里~


通讯录的实现

  • 基于动态顺序表实现通讯录项目
    • 1、功能要求
    • 2、代码实现
      • file.h
      • file.c
      • List.h
      • List.c
      • test.c

基于动态顺序表实现通讯录项目

准备:结构体、动态内存管理、顺序表、文件操作

1、功能要求

①能够存储100个人的通讯信息
②能够保存用户信息:名字、性别、年龄、电话、地址等
③增加、删除、查找、修改联系人
④显示联系人信息

2、代码实现

当我们将程序关闭时我们可以使用文件流的方法存储通讯录,使得通讯录信息不会丢失
我们在实现通讯录的时候,底层逻辑其实还是顺序表,我们将顺序表稍作修改,然后作为整个项目的其中一个.c文件和一个.h文件
我在实现的过程中,把顺序表相关文件命名为file,通讯录相关文件命名为List,调试文件为test,然后file就是上文顺序表中写出的代码稍加修改,本文对顺序表内容不做过多介绍,详细请参阅上文顺序表

file.h

#pragma once
#define INIT_CAPACITY 4
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "List.h"
typedef PeoInfo SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
    SLDataType* a;
    int size;     // 有效数据个数
    int capacity; // 空间容量
}SL;

//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);

//头部插入删除 / 尾部插入删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);

//指定位置之前插入/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFindByName(SL* ps, char* x);

file.c

#define _CRT_SECURE_NO_WARNINGS
#include "file.h"
void SLInit(SL* ps)
{
	ps->a = NULL;//将指针a置为空
	ps->capacity = 0;//指针没有指向任何数据,容量为0
	ps->size = 0;//同上,无数据,为0
}
void SLDestroy(SL* ps)
{
	if (ps->a)
	{
		free(ps->a);//释放a指向堆区的内存
		ps->a = NULL;//将a指针置为空
	}
	if (ps->capacity)
		ps->capacity = 0;
	if (ps->size)
		ps->size = 0;
}
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", *(ps->a + i));
	}
}
void SLCheckCapacity(SL* ps)
{
	if (ps->capacity == ps->size)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//ss
		//检查capacity是否为0,若为0,则开辟一块空间,若不为0,则将空间扩大一倍
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		//使用realloc函数开辟动态内存
		if (tmp == NULL)
		{
			perror(tmp);
			return 1;
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}
void SLPushFront(SL* ps, SLDataType x)
{
	if (ps->a)//确保不为空第一种方法
	{
		SLCheckCapacity(ps);
		for (int i = ps->size; i > 0; i--)
		{
			*(ps->a + i) = *(ps->a + i - 1);//从最后一位与空位开始交换,直到第一位空出来
		}
		*ps->a = x;//填补第一位
		ps->size++;
	}
}
void SLPopFront(SL* ps)
{
	if (ps->a)
	{
		for (int i = 0; i < ps->size - 2; i++)
		{
			*(ps->a + i) = *(ps->a + i + 1);
		}
		ps->size--;//将size减一最后一位就不会被访问到,将其在数组中删除
	}
}
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	*(ps->a + ps->size++) = x;//后置++先赋值后++,在最后一位放入x,然后size+1
}
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);
	ps->size--;//直接删除
}
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);//确保不为空第二种方法
	assert(pos >= 0 && pos <= ps->size);
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--)
	{
		*(ps->a + ps->size) = *(ps->a + ps->size - 1);
		//将要插入的数字位置后的所有数字往后移一位,然后插入
	}
	*(ps->a + pos) = x;
	ps->size++;//后置++
}
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	for (int i = pos; i < ps->size-1; i++)
	{
		ps->a[i] = ps->a[i+1];
	}
	//将所有指定位置以后的数字往前一位
	ps->size--;//直接删除最后一位
}
int SLFindByName(SL* ps, char* x)
{
	for (int i = 0; i < ps->size; i++)
	{
		if (strcmp(x, ps->a[i].name) == 0) // 使用 strcmp 函数来比较字符串  
		{
			return i;
		} // 如果找到匹配项,返回其索引  
	}
	return -1; // 如果未找到,返回 -1  
}


List.h

#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SeqList contact;
//用户数据
typedef struct PersonInfo
{
	char name[20];
	char sex[20];
	int age;
	char tel[20];
	char addr[20];
}PeoInfo;//定义一个用户信息结构体并重命名为PeoInfo
//初始化通讯录
void InitContact(contact* con);
//添加通讯录数据
void AddContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);

List.c

#define _CRT_SECURE_NO_WARNINGS
#include "List.h"
#include "file.h"
//初始化通讯录
void InitContact(contact* con)
{
	SLInit(con);//直接调用顺序表的方法,初始化通讯录
}
//添加通讯录数据
void AddContact(contact* con)
{
	PeoInfo i;//定义一个通讯录用户
	printf("请输入联系人姓名:\n");
	scanf("%s", i.name);
	printf("请输入联系人性别:\n");
	scanf("%s", i.sex);
	printf("请输入联系人年龄:\n");
	scanf("%d", &i.age);
	printf("请输入联系人电话:\n");
	scanf("%s", i.tel);
	printf("请输入联系人地址:\n");
	scanf("%s", i.addr);
	SLPushBack(con, i);
}
//删除通讯录数据
void DelContact(contact* con)
{
	char name[NAME_MAX];
	printf("请输入要删除的联系人姓名:\n");
	scanf("%s", name);//输入一个名字
	int f = SLFindByName(con, name);//使用SLFindByName函数找出这个名字对应的下标
	if (f < 0)
	{
		printf("要删除的联系人数据不存在!\n");
		return;//找不到就退出
	}
	SLErase(con, f);//找到就将它使用指定位置删除函数
	printf("删除成功!\n");
}
//展示通讯录数据
void ShowContact(contact* con)
{
	printf("%5s %5s %5s %5s %5s\n", "姓名", "性别", "年龄", "电话", "地址");
	for (int i = 0; i < con->size; i++)
	{
		printf("%5s %5s %5d %5s %5s\n",
			con->a[i].name,
			con->a[i].sex,
			con->a[i].age,
			con->a[i].tel,
			con->a[i].addr);
	}
}
//查找通讯录数据
void FindContact(contact* con)
{
	char name[NAME_MAX];
	printf("请输入要查找的联系人:\n");
	scanf("%s", name);
	int f = SLFindByName(con, name);
	if (f < 0)
	{
		printf("不存在\n");
		return;
	}
	printf("%5s %5s %5s %5s %5s\n", "姓名", "性别", "年龄", "电话", "地址");
	printf("%5s %5s %5d %5s %5s\n",
		con->a[f].name,
		con->a[f].sex,
		con->a[f].age,
		con->a[f].tel,
		con->a[f].addr);
}
//修改通讯录数据
void ModifyContact(contact* con)
{
	char name[NAME_MAX];
	printf("请输入需要修改的联系人姓名:\n");
	scanf("%s", name);
	int f = SLFindByName(con, name);//原理同上
	if (f < 0)
	{
		printf("不存在\n");
		return;
	}
	printf("新姓名:\n");
	scanf("%s", con->a[f].name);
	printf("新性别:\n");
	scanf("%s", con->a[f].sex);
	printf("新年龄:\n");
	scanf("%d", &con->a[f].age);
	printf("新电话:\n");
	scanf("%s", con->a[f].tel);
	printf("新地址:\n");
	scanf("%s", con->a[f].addr);
	printf("修改完成!\n");
}
//销毁通讯录数据
void DestroyContact(contact* con)
{
	SLDestroy(con);//直接调用顺序表销毁
}

test.c

#include "file.h"
#include "List.h"

void Test()
{
	contact con;
	InitContact(&con);
	AddContact(&con);
	ShowContact(&con);
	AddContact(&con);
	ShowContact(&con);
	AddContact(&con);
	ShowContact(&con);
	DelContact(&con);
	ShowContact(&con);
	FindContact(&con);
	ModifyContact(&con);
	ShowContact(&con);
	DestroyContact(&con);
}
int main()
{
	Test();
	return 0;
}

调试结果:
在这里插入图片描述
在这里插入图片描述

今天就分享到这里了~


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/563321.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Xilinx 7系列FPGA全局时钟缓冲器基本单元

Global Clock Buffer Primitives&#xff08;全局时钟缓冲器基本单元&#xff09;在FPGA&#xff08;现场可编程门阵列&#xff09;和其他数字系统中扮演着至关重要的角色。这些基本单元被设计用于处理、分配和增强时钟信号&#xff0c;以确保系统中的各个组件都能以精确和同步…

【Hadoop】- YARN架构[7]

前言 Yarn架构是一个用于管理和调度Hadoop集群资源的系统。它是Hadoop生态系统的一部分&#xff0c;主要用于解决Hadoop中的资源管理问题。 通过使用Yarn架构&#xff0c;Hadoop集群中的不同应用程序可以共享集群资源&#xff0c;并根据需要动态分配和回收资源。这种灵活的资…

4.21java聊天室项目小结

基本完成了用户的登录注册功能&#xff0c;可以实现用户账号登录和邮箱登录功能&#xff0c;忘记密码通过邮箱发送验证码找回&#xff0c;注册账号功能&#xff0c;并传递给客户端更新数据库的表内容 注册功能&#xff1a; 注册成功后密码进行MD5加密并通过服务器保存到数据库…

【STM32F4】STM32CUMX相关环境配置

一、环境配置 我们需要以下两个软件 &#xff08;一&#xff09;keil5 最正统&#xff0c;最经典的嵌入式MCU开发环境。 该环境的配置可以看看之前的文章 所需文件如下&#xff1a; 当时配置的是STC8H的环境&#xff0c;现在基于此&#xff0c;重新给STM32配置环境。能让STC…

Tricentis测试生成式人工智能系统和红队:入门指南

Tricentis测试生成式人工智能系统和红队:入门指南 测试人工智能并确保其责任、安全和保障的话题从未如此紧迫。自 2021 年以来,人工智能滥用的争议和事件增加了26 倍,凸显了日益增长的担忧。用户很快就会发现,人工智能工具并非万无一失。他们可能会表现出过度自信,并且缺…

ChatGPT研究论文提示词集合2-【形成假设、设计研究方法】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 目录 1.形成假设 2.设计研究方法 3.书籍介绍 AIPaperPass智能论文写作平台 近期小编按照学术论文的流程&#xff0c;精心准备一套学术研究各个流程的提示词集合。总共14个步骤&#…

11.接口自动化学习-Yaml学习

1.配置文件作用 配置文件项目角度&#xff1a; &#xff08;1&#xff09;现成的应用–第三方组件 mysql–数据库–my.conf tomcat–web服务器–server.xml 修改&#xff1a;连接数/端口 redis–缓存服务器–redis.conf 修改配置 jemeter–压测工具–jemeter.properties–修改…

PHP反序列化漏洞原理(附带pikachu靶场演示)

1.反序列化概念 序列化:是将变量转换为可保存或传输的字符串的过程;实现函数是serialize()反序列化:就是在适当的时候把这个字符串再转化成原来的变量使用&#xff0c;就是序列化的逆过程。实现函数是unserialize() 直白一点就是&#xff1a;序列化是把对象转换成字节流&#…

SpringMVC--RESTful

1. RESTful 1.1. RESTful简介 REST&#xff1a;Representational State Transfer&#xff0c;表现层资源状态转移。 RESTful是一种网络架构风格&#xff0c;它定义了如何通过网络进行数据的交互。这种风格基于HTTP协议&#xff0c;使得网络应用之间的通信变得更加简洁和高效。…

python爬虫--------requests案列(二十七天)

兄弟姐们&#xff0c;大家好哇&#xff01;我是喔的嘛呀。今天我们一起来学习requests案列。 一、requests____cookie登录古诗文网 1、首先想要模拟登录&#xff0c;就必须要获取登录表单数据 登录完之后点f12&#xff0c;然后点击network&#xff0c;最上面那个就是登录接口…

数据结构––kmp算法(串)

kmp算法作为串的一个重要内容&#xff0c;必然有一定的难度&#xff0c;而在看到各类教辅书里的概念与解释后&#xff0c;其晦涩难懂的内容直接劝退一部分人&#xff0c;现在&#xff0c;让我们来看看吧 KMP解决的问题类型 KMP算法的作用就是在一个已知的字符串中查找子串的位…

布控球——防爆监控设备

控球&#xff0c;作为一种专为特定场景设计的防爆监控设备&#xff0c;主要用于在高风险、易燃易爆等特殊环境中提供实时、安全、高效的视频监控服务。其主要特点和功能如下&#xff1a; 防爆性能&#xff1a;布控球首先具备出色的防爆能力&#xff0c;外壳通常采用高强度、耐高…

解开Intel ECI 的面纱

前言 Intel ECI是一个用于工业领域边缘控制的软硬件平台&#xff0c;我们今天主要探索的是软件部分&#xff0c;也就是系统镜像。区别于传统的Ubuntu或者Debian&#xff0c;ECI的强大之处在于它的实时性以及对于Intel自家芯片的缓存优化能力极强。 那么让我们来探索一下 编译…

学习51单片机 C语言知识

一、数据类型 C 语言包含的数据类型如下图所示 C51 的数据类型分为基本数据类型和组合数据类型&#xff0c;情况与标准 C 中的数据类型基本相同&#xff0c;但其中 char 型与 short 型相同&#xff0c;float 型与 double 型相同&#xff0c;另外&#xff0c;C51 中还有专门针…

2、JVM 类加载机制深度剖析

今天我们就来看看JVM的类加载机制到底是怎么样的&#xff0c;搞清楚这个过程了&#xff0c;那么以后在面试时&#xff0c;对面试官常问的JVM类加载机制&#xff0c;就能把一些核心概念说清楚了。 2.1、JVM在什么情况下会加载一个类&#xff1f; 类加载过程虽然繁琐复杂&#…

一文读懂Partisia Blockhain:兼顾去中心化、安全性与可扩展性

“Partisia Blockhain 解决了区块链领域长期存在的问题&#xff0c;其兼顾了去中心化、安全性以及可扩展性” Partisia Blockchain 是一个具有独特零知识证明预言机以及分片解决方案的 Layer1&#xff0c;解决了困扰整个区块链行业的问题。 目前&#xff0c;多样化的区块链层出…

【Linux驱动层】iTOP-RK3568学习之路(二):vscode中设置头文件路径-完成代码自动补全

在Ubuntu下用vscode写Linux驱动层的时候&#xff0c;需要添加头文件&#xff1a; #include<linux/module.h> #include<linux/init.h> #include<linux/kernel.h>但vscode没有智能提示&#xff0c;因此需要我们手动添加自己的头文件路径&#xff1a; topeetu…

个人主页源码 翻盖式LOGO

源码介绍 衍生自 Vno Jekyll 主题页面部分加载效果借鉴于 Mno Ghost 主题借鉴了北岛向南的小屋的头像样式主页的 Logo 字体已经过压缩&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改 效果截图 源码下载 个人主页源码 翻盖式LOGO…

【C++】双指针算法:盛最多水的容器

1.题目 2.算法思路 有两种方法&#xff1a; 第一种&#xff1a; 暴力穷举法&#xff0c;就是用两次循环将所有的可能性算出来&#xff0c;然后求出最大值。 这种方法最容易想到&#xff0c;但时间复杂度是O(n^2)&#xff0c;一定会超时的&#xff01; 第二种&#xff1a; …

【面试经典 150 | 数组】最后一个单词的长度

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;遍历 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容进行回顾…