【C语言】通讯录(静态版本+动态版本)思路解析+完整源代码

news2024/10/5 2:33:29

通讯录

由于代码比较长,为了增加可读性,分成了contact.h,contact.c,test.c,分别用来声明函数或者类型,实现函数功能,测试代码

contact.h

我们希望通讯录具有增加联系人,删除联系人,显示联系人,找查联系人,修改联系人,排序的功能,联系人的信息具有名字,年龄,性别,电话,地址的信息

由于每次对通讯录进行操作都要把data数组和存的联系人的个数sz传过去,那干脆把他们两个打包到一个结构体类型struct contact里面

枚举类型是为了增加test.c里面switch语句选项的可读性,默认第一个成员也就是EXIT就是0,那么我们就可以把switch语句里面的0换成EXIT了。

test.c

contact.c

首先把所有data数组的所有内容以及p->sz初始化成0

增加联系人的函数

一次性增加一个联系人的信息

数字表示打印多少位,负号表示左对齐,\t表示插入一个制表符,能够让每一行对齐

在查找联系人,删除联系人,修改联系人信息的函数中,我们都需要先找到某个联系人,为了避免写三份类似的代码,我们这里使用了一个函数。

查找联系人的函数

修改信息的函数

排序的函数

像这样静态版本的通讯录问题还是比较大的,首先就是我们不管存多少个联系人的信息,上来都创建了一个100个元素的数组,这样对内存的开销就比较大,如果我们存的信息少,就浪费了内存,如果存的太多,又要去修改最大容量,在我们学习了动态内存开辟之后,我们可以对通讯录进行如下修改:通讯录刚上来可以存放三个联系人的信息,当通讯录满了之后,自动扩充两个人的名额。于是我们就可以把data数组改成一个指针,为了尽可能少的改动原来的代码,我们把这个指针的名字也叫做data。这与原来数组名的含义都是地址,现在data是一个people*类型的指针,我们可以把使用malloc,realloc函数开辟的内存首地址存到里面去。

那么我们先对以前的contact类型进行以下修改

实际上需要修改的函数只有初始化通讯录的函数和增加联系人的函数。

首先使用malloc开辟一块能存放三个people类型变量的空间,并返回首地址存到data里面去。这里申请了空间由于后面要用,所以并没有及时释放掉,最后关闭通讯录的时候再释放即可。

然后是增加联系人的函数,在增加之前我们先要判断一下通讯录是不是已经满了,我们使用下面的函数来实现这个功能

由于realloc增容可能会失败,因此必须检查,在增容完毕之后要应该更改掉当前的最大容量capacity。

然后在添加联系人的函数中调用这个检查容量的函数

由于以上所有动态内存开辟的空间都没有被释放掉,因此我们在推出这个通讯录的时候应该及时释放掉,在test函数这里调用一个用来释放内存的函数

这个函数也是需要我们自己编写的

这样我们的通讯录就是动态版本的了。

源代码

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void menu() {
	printf("******1.add*****2.del***********\n");
	printf("******3.search**4.modify********\n");
	printf("******5.show****6.sort**********\n");
	printf("***********0.exit***************\n");
} 
void test() {
	int input = 0;
	//创建通讯录
	contact con = {0};
	do {
		menu();
		printf("请选择功能\n");
		scanf("%d", &input);
		switch(input) {
		case ADD: 
			addcontact(&con);
			break;
		case DEL:
			delcontact(&con);
			break;
		case SEARCH:
			searchcontact(&con);
			break;
		case MODIFY:
			modifycontact(&con);
			break;
		case SHOW:
			showcontact(&con);
			break;
		case SORT:
			sortcontact(&con);
			break;
		case EXIT:
			release(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("输入错误,请重新选择\n");
		}
	} while (input);
}
int main() {
	test();
	return 0;
}

contact.c

#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
//初始化通讯录的函数
void initcontact(contact* p) {
	p->data = (people*)malloc(DEFAULT_NUM *sizeof(people));//刚上来容量是3
	if (p->data == NULL) {
		perror("initcontact");
		return;
	}
	p->sz = 0;
	p->capacity = DEFAULT_NUM;
}
int check_capacity(contact*p) {
	if (p->capacity == DEFAULT_NUM) {
		//扩大容量
		people* ptr=(people*)realloc(p->data, (DEFAULT_NUM + INC) * sizeof(people));
		//一个变量名为ptr的people*类型指针,用来接收调整之后的空间首地址
		if (ptr == NULL) {
			perror(check_capacity);
			return 0;
		}
		else {
			p->data = ptr;//这个ptr可能是原来的data,也可能不是,增容成功之后赋给data
			p->capacity += INC;
			return 1;
		}
	}
	return 1;//压根不需要增容,也返回1
}
//添加联系人的函数,动态版本
void addcontact(contact* p) {
	int ret = check_capacity(p);
	if (0 == ret) {
		return;
	}
	printf("请输入名字\n");
	scanf("%s", p->data[p->sz].name);
	printf("请输入年龄\n");
	scanf("%d", &(p->data[p->sz].age));
	printf("请输入性别\n");
	scanf("%s", p->data[p->sz].sex);
	printf("请输入电话号码\n");
	scanf("%s", p->data[p->sz].tele);
	printf("请输入地址\n");
	scanf("%s", p->data[p->sz].address);
	p->sz++;
	printf("增加联系人成功\n");
}
//显示联系人的函数
void showcontact(const contact* p) {
	int i = 0;
	for (i = 0; i < p->sz; i++) {
		//打印标题
		printf("%-10s\t,%-4s\t,%-5s\t,%-12s\t,%-30s\n", "名字", "年龄", "性别", "电话", "地址");
		printf("%-10s\t,%-4d\t,%-5s\t,%-12s\t,%-30s\n",
			p->data[i].name,
			p->data[i].age,
			p->data[i].sex,
			p->data[i].tele,
			p->data[i].address
		);
	}
}
//通过名字找查联系人的函数
// 由于在delcontact,searchcontact,modifycontact函数中都需要找查
// 为了避免写三份类似的代码,我们使用函数来实现找查联系人的功能
int find_by_name(contact* p, char name[]) {
	//找查联系人
	int i = 0;
	for (i = 0; i < p->sz; i++) {
		if (strcmp(p->data[i].name, name) == 0) {
			return i;//找到了,返回下标
		}
	}
	return -1;//没有找到,返回-1
}
//删除联系人的函数(暂时不考虑两个人的名字相同的情况)
void delcontact(contact* p) {
	char name[20] = { 0 };
	printf("请输入要删除的人名字\n");
	scanf("%s", name);
	int i = 0;
	int del = 0;//记录要删除的人信息在data数组中的下标
	int flag = 0;
	if (p->sz == 0) {
		printf("通讯录为空,无法删除\n");
	}
	//找查联系人
	del = find_by_name(p, name);
	//删除联系人
	if (del == -1) {
		printf("要删除的人不存在\n");
		return;
	}
	for (i = del; i < p->sz - 1; i++) {
		p->data[i] = p->data[i + 1];//循环用后面的元素覆盖前面的元素
	}
	p->sz--;
	//如果要删除最后一个联系人,也就是下标为sz-1的那个人的信息
	//由于刚上来del就是sz-1,不会进入循环,也就不会覆盖掉最后一个元素
	//但是sz--了,就访问不到最后一个元素了,效果上就好像删除了最后一个联系人
	printf("删除成功\n");
}
//查找某个联系人的函数
void searchcontact(contact* p) {
	char name[20] = { 0 };
	printf("请输入要查找的人的名字\n");
	scanf("%s", name);
	int pos = find_by_name(p, name);//返回的就是要找的这个人的信息在data数组中的下标 
	if (pos == -1) {
		printf("查无此人\n");
		return;
	}
	else {
		//打印这一个人的信息
		printf("%-10s\t,%-4s\t,%-5s\t,%-12s\t,%-30s\n", "名字", "年龄", "性别", "电话", "地址");
		printf("%-10s\t,%-4d\t,%-5s\t,%-12s\t,%-30s\n",
			p->data[pos].name,
			p->data[pos].age,
			p->data[pos].sex,
			p->data[pos].tele,
			p->data[pos].address
		);
	}
}
//修改信息的函数
void modifycontact(contact* p) {
	char name[20] = { 0 };
	printf("请输入要修改信息的人的名字\n");
	scanf("%s", name);
	int pos = find_by_name(p, name);
	if (pos == -1) {
		printf("查无此人\n");
	}
	else {
		printf("请输入名字\n");
		scanf("%s", p->data[pos].name);
		printf("请输入年龄\n");
		scanf("%d", &(p->data[pos].age));
		printf("请输入性别\n");
		scanf("%s", p->data[pos].sex);
		printf("请输入电话号码\n");
		scanf("%s", p->data[pos].tele);
		printf("请输入地址\n");
		scanf("%s", p->data[pos].address);
		printf("修改联系人信息成功\n");
	}
}
//qsort需要的比较大小的函数
int cmp_by_name(void* str1,void* str2) {
	return strcmp(((people*)str1)->name, ((people*)str2)->name);
}
//根据名字对信息排序的函数
void sortcontact(contact* p) {
	qsort(p->data, p->sz, sizeof(p->data[0]), cmp_by_name);
	printf("排序成功\n");
}
void release(contact* p) {
	free(p->data);
	p->data = NULL;//注意是data指向的空间被释放掉,不是p指向的空间被释放掉
	p->capacity = 0;
	p->sz = 0;
}

contact.h

#pragma once
//用于各种函数或者类型的声明
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100 //通讯录能添加的最大人数
#define DEFAULT_NUM 3//刚上来的默认容量是3
#define INC 2//到达最大容量的时候一次性扩充的个数
typedef struct people {
	char name[20];
	int age;
	char sex[5];
	char tele[12];
	char address[30];
}people;//创建结构体类型并重命名为people
typedef struct contact {
	people* data;//一个名为data的指针变量
	int sz;//用来记录通讯录里面存了几个人的信息了
	int capacity;//记录当前最大容量
}contact;
//枚举类型,增加可读性,默认EXIT就是0
enum OPTION {
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
};
void initcontact(contact* p);//初始化通讯录的函数,把通讯录中内容初始化为0
void addcontact(contact* p);//增加联系人的函数
void showcontact(const contact* p);//显示所有联系人的函数,显示并不会修改p指向的内容,因此加了const
void delcontact(contact* p);//删除联系人的函数
void searchcontact(contact* p);//找查某个联系人的函数
void modifycontact(contact* p);//修改信息的函数
void sortcontact(contact* p);//根据名字对信息排序的函数
void release(contact* p);//使用完成之后释放动态申请的那些空间的函数

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

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

相关文章

防火墙之firewalld基础

一、firewalld的简介 firewalld防火墙是Centos7系统默认的防火墙管理工具&#xff0c;取代了之前的iptables防火墙&#xff0c;也是工作在网络层&#xff0c;属于包过滤防火墙。 firewalld和iptables都是用来管理防火墙的工具&#xff08;属于用户态&#xff09;来定义防火墙的…

css pointer-events 多层鼠标点击事件

threejs 无法滑动视角&#xff0c;菜单界面覆盖threejs操作事件。 pointer-events /* Keyword values */ pointer-events: auto; pointer-events: none; pointer-events: visiblePainted; /* SVG only */ pointer-events: visibleFill; /* SVG only */ pointer-events: visib…

web基础及http协议 (二) apache

一、httpd 安装组成 http 服务基于 C/S 结构 1 .常见http 服务器程序 httpd apache&#xff0c;存在C10K&#xff08;10K connections&#xff09;问题 nginx 解决C10K问题lighttpd IIS .asp 应用程序服务器 tomcat .jsp 应用程序服务器 jetty 开源的servlet容器&#xf…

Linux 驱动开发基础知识——LED 模板驱动程序的改造:设备树(十一)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;Vir2021GKBS &#x1f43c;本文由…

探索线性回归中的梯度下降法

目录 前言1 梯度下降的基本思想2 梯度下降的公式3 梯度下降的步骤3.1 初始化参数3.2 计算梯度3.3 更新参数3.4 迭代更新 4 学习率的控制4.1 过大学习率的问题4.2 过小学习率的问题4.3 学习率的调整 5 批量梯度下降方法5.1 批量梯度下降&#xff08;Batch Gradient Descent&…

代码随想录算法训练营DAY20 | 二叉树(7) (续)

一、LeetCode 236 二叉树的最近公共祖先 题目链接&#xff1a;236.二叉树的最近公共祖先https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/ 思路&#xff1a;利用后序遍历是天然回溯过程、方便实现自底向上查找的原理&#xff0c;递归寻找公…

基于SpringBoot的高校竞赛管理系统

基于SpringBoot的高校竞赛管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 个人中心 管理员界面 老师界面 摘要 高校竞赛管理系统是为了有效管理学校…

书生开源大模型-第2讲-笔记

1.环境准备 1.1环境 先克隆我们的环境 bash /root/share/install_conda_env_internlm_base.sh internlm-demo1.2 模型参数 下载或者复制下来&#xff0c;开发机中已经有一份参数了 mkdir -p /root/model/Shanghai_AI_Laboratory cp -r /root/share/temp/model_repos/inter…

分库分表浅析

简介 对于任何系统而言&#xff0c;都会设计到数据库随着时间增长而累积越来越多的数据&#xff0c;系统也因为越来越多的需求变迁导致原有的设计不再满足现状&#xff0c;为了解决这些问题&#xff0c;分库分表就会走进视野&#xff0c;带着几个问题走入分库分表。 什么是分…

嵌入式学习第十八天(目录IO)

目录IO: 1. mkdir int mkdir(const char *pathname, mode_t mode); 功能&#xff1a;创建目录文件 参数&#xff1a; pathname&#xff1a;文件路径 mode&#xff1a;文件的权限 rwx rwx rwx 111 111 111 0 7 7 7 r&#xff1a;目录中是否能够查看文件 w&#xff1a;目…

瑞_23种设计模式_代理模式

文章目录 1 代理模式&#xff08;Proxy Pattern&#xff09;1.1 介绍1.2 概述1.3 代理模式的结构 2 静态代理2.1 介绍2.2 案例——静态代理2.3 代码实现 3 JDK动态代理★★★3.1 介绍3.2 代码实现3.3 解析代理类3.3.1 思考3.3.2 使用 Arthas 解析代理类3.3.3 结论 3.4 动态代理…

ARM体系在linux中的中断抢占

上一篇说到系统调用等异常通过向量el1_sync做处理&#xff0c;中断通过向量el1_irq做处理&#xff0c;然后gic的工作都是为中断处理服务&#xff0c;在rtos中&#xff0c;我们一般都会有中断嵌套和优先级反转的概念&#xff0c;但是在linux中&#xff0c;中断是否会被其他中断抢…

RTC时钟

目录 一、STM32F407内部RTC硬件框图&#xff0c;主要由五大部分组成&#xff1a; 二、硬件相关引脚 三、具体代码设置步骤 四、了解其它知识点 一、STM32F407内部RTC硬件框图&#xff0c;主要由五大部分组成&#xff1a; ① 时钟源 (1)LSE&#xff1a;一般我们选择 LSE&am…

网络编程_TCP通信综合练习:

1 //client&#xff1a;&#xff1a; public class Client {public static void main(String[] args) throws IOException {//多次发送数据//创建socket对象,填写服务器的ip以及端口Socket snew Socket("127.0.0.1",10000);//获取输出流OutputStream op s.getOutput…

python统计分析——一元线性回归分析

参考资料&#xff1a;用python动手学统计学 1、导入库 # 导入库 # 用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 用于绘图的库 import matplotlib.pyplot as plt import seaborn as sns sns.set() # 用于估计统计…

【高效开发工具系列】PyCharm使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

vue3项目配置按需自动导入API组件unplugin-auto-import

场景应用&#xff1a;避免写一大堆的import&#xff0c;比如关于Vue和Vue Router的 1、安装unplugin-auto-import npm i -D unplugin-auto-import 2、配置vite.config import AutoImport from unplugin-auto-import/vite//按需自动加载API插件 AutoImport({ imports: ["…

Unity中的Lerp插值的使用

Unity中的Lerp插值使用 前言Lerp是什么如何使用Lerp 前言 平时在做项目中插值的使用避免不了&#xff0c;之前一直在插值中使用存在误区&#xff0c;在这里浅浅记录一下。之前看的博客或者教程还多都存在一个“永远到达不了&#xff0c;只能无限接近”的一个概念。可能是之前脑…

ThreadLocal “你”真的了解吗?

今天想梳理一个常见的面试题。在开始之前&#xff0c;让我们一起来回顾一下昨天的那篇文章——《Spring 事务原理总结七》。这篇文章比较啰嗦&#xff0c;层次也不太清晰&#xff0c;所以以后有机会我一定要重新整理一番。这篇文章主要想表达这样一个观点&#xff1a;Spring的嵌…

对于软件测试的理解

前言 “尽早的介入测试&#xff0c;遇到问题的解决成本就越低” 随着软件测试技术的发展&#xff0c;测试工作由原来单一的寻找缺陷逐渐发展成为预防缺陷&#xff0c;探索测试&#xff0c;破坏程序的过程&#xff0c;测试活动贯穿于整个软件生命周期中&#xff0c;故称为全程…