首页 > 编程语言 >经典程序:通讯录的编写

经典程序:通讯录的编写

时间:2024-11-20 14:43:52浏览次数:3  
标签:sz printf pc Contact 通讯录 经典 编写 data

1.引言

在我们学完了文件操作,指针,以及结构体等等知识后,尝试写出一个通讯录程序,并且能实现以下功能:增加通讯录,删除通讯录,查找通讯录,显示通讯录,退出程序,下面我们来进行代码实现;

2.test.c源文件的编写

首先我们想把通讯录页面打印出来,那么我们可以定义一个input变量,并且用do...while循环来循环整个过程,用menu函数来打印菜单,用switch语句来分不同情况,但我们可以将平常我们习惯使用的单个整形来表示不同的情况,例如:case 0: case 1: ......,将数字替换成枚举常量,枚举常量可以代表不同的含义,可以起到提示我们撰写函数名的作用,那么我们就可以定义一个enum INPUT的枚举类型,这里值得注意的是,如果退出的选项对应的枚举常量为exit的话,会与库函数exit起冲突,发生错误,这里我们统一用大写来定义枚举常量,这里我们先定义一个结构体变量,稍后我们会在contact.c中具体介绍,然后使用一个初始化函数来初始化结构体,再根据不同的情况,写出我们将在contact.c中定义的函数的函数名,到此,源文件test.c的撰写就结束了,下面是具体代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
	printf("*************************************\n");
	printf("********     CONTACT PLUS     *******\n");
	printf("********  1. add     2. del    ******\n");
	printf("********  3. search  4. modify ******\n");
	printf("********  5. show    0. exit   ******\n");
	printf("*************************************\n");
	printf("*************************************\n");
}
enum INPUT
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
Contact con;
int main()
{
	int input = 0;
	Initcontact(&con);
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
			case EXIT:
				break;
			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;
		}
	} while (input);
	return 0;
}

3.contact.c源文件的编写以及各函数的实现

1.Initcontact函数的编写

这里就不得不提及到我们在test.c源文件定义的结构体变量了,我们想在通讯录中记录名字,性别,电话,以及住址等信息,那么我们就定义一个结构体,并将其重命名:

typedef struct peoinfo
{
	char name[20];
	char sex[10];
	char tele[13];
	char addr[40];
}Peo;

如果想对通讯录进行操作,我们还需要知道通讯录中成员的具体数量,那么我们就将数量sz和结构体数组data一起定义成一个新的结构体:

typedef struct contact
{
	Peo data[MAX];
	int sz;
}Contact;

这样,定义完结构体后,我们就可以对结构体进行初始化了,这里我们取con的地址传给函数,那么就用Contact*这个类型的指针去接收,使用指向操作符对结构体成员进行访问,pc->sz=0;

再利用memset将pc->data初始化,大小计算可以使用sizeof(pc->data),这样,完成了函数的编写:

void Initcontact(Contact* pc)
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

2.Addcontact函数的编写

在添加通讯录成员前,我们需要在函数开始判断是否放得下将要添加的成员,如果sz等于了我们#define的MAX,那么就显示满了,无法添加,如果不满足这一个条件,那么允许添加,我们scanf每一个成员,最后sz++,代表添加成功,成员数+1,这里值得注意的是,字符串在这里本身就代表首元素地址,不必再用&,但如果是整形,仍然需要使用&符号:

void Addcontact(Contact* pc)
{
	if (pc->sz >= MAX)
	{
		printf("contact满了\n");
	}
	else
	{
		printf("请输入name:\n");
		printf("请输入sex:\n");
		printf("请输入tele:\n");
		printf("请输入addr:\n");
		scanf("%s\n%s\n%s\n%s", pc->data[pc->sz].name, pc->data[pc->sz].sex, pc->data[pc->sz].tele, pc->data[pc->sz].addr);
		pc->sz++;
	}
}

3.Delcontact函数的编写

如果我们想要删除通讯录中的某一个成员,首先要让用户输入想要删除成员的名称,然后在整个结构体中去查找这个成员,如果找到了那么就进行下一步,没有找到就结束,下一步,将这个成员后面所有成员分别依次向前赋值,注意在结构体成员中,整形数据是可以直接赋值的,但是字符串等等数据就需要用到memmove,memcpy,strcpy这样的函数去操作,同时将sz自减一个,就可以实现删除的功能:

void Delcontact(Contact* pc)
{
	printf("请输入你想删除的名字:\n");
	char input[10] = { 0 };
	gets(input);
	int i = 0;
	int flag = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(input, pc->data[i].name) == 0)
		{
			flag = 1;
			break;
		}
	}
	if (flag == 0)
	{
		printf("没有找到,你在搞什么啊!\n");
	}
	else
	{
		int j = 0;
		for (j = i; j < pc->sz - 1; j++)
		{
			strcpy(pc->data[j].name , pc->data[j + 1].name);
			strcpy(pc->data[j].sex, pc->data[j + 1].sex);
			strcpy(pc->data[j].tele, pc->data[j + 1].tele);
			strcpy(pc->data[j].addr, pc->data[j + 1].addr);
		}
		pc->sz--;
	}
}

4.Searchcontact函数的编写

这一函数相对前几个函数比较简单,只需要在结构体中查找与用户输入字符串相同的结构i成员组,如果找到那么就将这一结构体打印出来,如果没有找到则不打印,直接给出代码:

void Searchcontact(Contact* pc)
{
	printf("请输入你想找到的名字:\n");
	char input[10] = { 0 };
	scanf("%s", &input);
	int i = 0;
	int flag = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(input, pc->data[i].name) == 0)
		{
			flag = 1;
			printf("嘿,你小汁!\n");
			printf("%-10s\t%-10s\t%-12s\t%-40s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
			break;
		}
	}
	if (flag == 0)
	{
		printf("没有找到,你在搞什么啊!\n");
	}
}

5.Modifycontact函数的编写

想要修改通讯录中的成员信息,也是先要找到,重复与前两个函数同样的动作,其实查找成员最好是写成一个新函数,否则这样显得麻烦和累赘,这也是我的通讯录中需要改进的地方,找到之后,重新scanf每个结构体的信息,将新的信息覆盖旧的信息,其他的地方则不加改动:

void Modifycontact(Contact* pc)
{
	printf("请输入你想修改的名字:\n");
	char input[10] = { 0 };
	scanf("%s", &input);
	int i = 0;
	int flag = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(input, pc->data[i].name) == 0)
		{
			flag = 1;
			printf("嘿,你小汁!\n");
			printf("请输入name:\n");
			printf("请输入sex:\n");
			printf("请输入tele:\n");
			printf("请输入addr:\n");
			scanf("%s\n%s\n%s\n%s", pc->data[i].name, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
			break;
		}
	}
	if (flag == 0)
	{
		printf("没有找到,你在搞什么啊!\n");
	}
}

4.升级初级通讯录

学完文件操作和动态内存开辟之后,我们可以来对我们的初阶通讯录进行一个升级,那么大多数函数和结构是不需要改动的,需要改动的是下面几个部分:

1.升级Contact结构体

如果我们想要对结构体所占空间实现一个动态分配和开辟,加载通讯录中以及有的内容,那么我们首先就要对Contact结构体进行升级,首先我们可以把data改为Peo*类型的指针,再来增添一个capacity来表示开辟空间后所有容纳的成员数量,代码如下:

typedef struct contact
{
	Peo* data;
	int sz;
	int capacity;
}Contact;

2.升级Initcontact函数

前面既然定义了一个Peo*的指针,想要放下结构体成员,那么肯定要用到动态内存开辟函数calloc,在开辟的同时对结构体进行初始化,首先我们开辟一个设定的初始容量,用tmp指针接收,如果为NULL,说明空间开辟失败,那么就结束这个函数,开辟成功,我们就对通讯录进行一个信息加载,用到Loadcontact函数,在Loadcontact函数中,我们用fopen来打开文件,用fread来读取数据,这里我们每次让fread读取一次,如果fread的返回值为0,就说明读取结束了,在读取数据的同时,我们还要检查内存是否够用,于是就有了checkdata函数,此函数中,如果sz等于了capacity那么就要进行扩容,一次扩容inc_sz这么多个结构体大小,随后capacity加上inc_sz,整体如下:

void Loadcontact(Contact* pc)
{
	FILE* tmp = fopen("Contact_Plus.txt", "rb");
	if (tmp == NULL)
	{
		printf("%s", strerror(errno));
		return;
	}
	else
	{
		Peo p;
		int i = 0;
		//fread的返回值为读到的数据个数
		while (fread(&p, sizeof(Peo), 1, tmp))
		{
			checkdata(pc);
			pc->data[i] = p;
			pc->sz++;
			i++;
		}
		fclose(tmp);
		tmp = NULL;
	}
}
void Initcontact(Contact* pc)
{
	pc->sz = 0;
	Peo* tmp = (Peo*)calloc(strat_sz, sizeof(Peo) * strat_sz);
	if (tmp == NULL)
	{
		perror("calloc");
	}
	else
	{
		pc->data = tmp;
		pc->capacity = strat_sz;
	}
	//加载信息到通讯录
	Loadcontact(pc);
}

void checkdata(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		Peo* tmp = (Peo*)realloc(pc->data, sizeof(Peo) * (pc->capacity + inc_sz));
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		printf("扩容成功!\n");
		pc->data = tmp;
		pc->capacity += inc_sz;
	}
}

3.增添Savecontact函数

在对通讯录编辑完成后,我们将数据写入到我们的文件中,保存到磁盘中,这里我给出两个写法,一个是使用二进制形式对文件打开,使用fwrite进行写入,或者使用只写形式打开,使用fprintf函数进行格式化写入,如下:

void Savecontact1(Contact* pc)
{
	FILE* tmp =fopen("Contact_Plus.txt", "wb");
	if (tmp == NULL)
	{
		perror("fopen");
	}
	else
	{
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data, sizeof(Peo), 1, tmp);
		}
		fclose(tmp);
		tmp = NULL;
	}
}

void Savecontact2(Contact* pc)
{
	FILE* tmp = fopen("Contact_Plus.txt", "w");
	if (tmp == NULL)
	{
		perror("fopen");
		return;
	}
	else
	{
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fprintf(tmp ,"%s %s %s %s", pc->data[i].name, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
		}
	}
	fclose(tmp);
	tmp = NULL;
}

4.增添Destroycontact函数

使用那么多动态开辟指针,我们需要使用free释放,然后主动置为空指针,养成这样的好习惯,可以避免内存泄露和野指针的引用:

void Destroycontact(Contact* pc)
{
	pc->sz = 0;
	pc->capacity = 0;
	free(pc->data);
	pc->data = NULL;
}

5.contact.h的总结

将各个函数都在头文件中进行声明,以及结构体的定义,这里我们一并给出:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<windows.h>
#include<time.h>

#define strat_sz 3
#define inc_sz 2

#define MAX 100

typedef struct peoinfo
{
	char name[20];
	char sex[10];
	char tele[13];
	char addr[40];
}Peo;

//typedef struct contact
//{
//	Peo data[MAX];
//	int sz;
//}Contact;

typedef struct contact
{
	Peo* data;
	int sz;
	int capacity;
}Contact;

void Initcontact(Contact* pc);

void Addcontact(Contact* pc);

void Delcontact(Contact* pc);

void Searchcontact(Contact* pc);

void Modifycontact(Contact* pc);

void Showcontact(Contact* pc);

void checkdata(Contact* pc);

void Destroycontact(Contact* pc);

void Savecontact1(Contact* pc);

void Savecontact2(Contact* pc);

通讯录程序到此结束,谢谢读者们的支持!

标签:sz,printf,pc,Contact,通讯录,经典,编写,data
From: https://blog.csdn.net/Dai_renwen/article/details/143866044

相关文章

  • 用Python编写一个websocket客户端应用
    前两天发了一篇《用Python做一个websocket服务端》,起了一个websocket服务。然后又发了一篇《用jquery做一个websocket客户端》,这是直接在网页中验证websocket服务是否有效。但是,对于客户端怎么实际应用websocket并没有涉及。作为一个轻微强迫症者,我觉得还是要再捣鼓一下websock......
  • Spring Cloud 经典面试题
    一、谈谈SpringCloud优缺点? SpringCloud的优点是:集成度高、生态丰富、可扩展性强、功能全面。SpringCloud的缺点是:学习曲线陡峭、有一定的性能开销、组件迭代快版本多、管理复杂。集成度高:SpringCloud集成了多个成熟的微服务组件(如Eureka、Zuul、Ribbon、Hystrix、Sl......
  • HAL层代码编写注意事项
    基本架构#include<hardware/hardware.h>#include<fcntl.h>#include<cutils/log.h>//HAL规定不能直接使用hw_module_t结构体//因此需要在hw_module_t外再套一层结构体,这也是HAL要求的structmy_module_t{//hw_module_t结构体表示HAL模块的基本信息,成员变量可以任意......
  • 剑指RSA——量子计算Shor算法对经典密码体制的威胁
    摘  要整数分解是数论中一个古老且复杂的问题,当今世界上最著名且广泛使用的RSA公钥密码体制,其安全性依赖于对极大整数进行因数分解的困难性。经典计算机在分解极大整数时所需的时间成本与信息价值不成正比,这使得RSA算法的安全性得以维持。然而,随着量子计算的迅猛发展,传统......
  • #渗透测试#SRC漏洞挖掘#网络运维# 黑客脚本编写05之字符串运算符与逻辑运算
    免责声明本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章阅读。                            ......
  • 经典C语言代码——part 2
    【程序4】题目:输入某年某月某日,判断这一天是这一年的第几天?1.程序分析:以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本年的第几天,特殊情况,闰年且输入月份大于3时需考虑多加一天。2.程序源代码:main(){intday,month,year,sum,leap;printf("\nplease......
  • 汇编语言-实验10编写子程序
    名称:show_str功能,在指定的位置,用指定的颜色,显示一个用0结束的字符串。参数:(dh)行号。(dl)列号,(cl)颜色ds:si指向字符串首地址返回无应用举例:8行3列,用绿色显示data中的字符串代码如下:assumecs:codedatasegmentdb'Welcometomasm!',0dataendscodesegmentstart:movdh,8......
  • Windows基础及bat蠕虫病毒编写
    常见端口及其服务21ftp23talnet80web80-89都可能443ssl有过心脏滴血漏洞445msb1433mssql1521oracle2082/3主机管理系统登录(国外用的多)2222da虚拟主机管理系统登录(国外较多)3128squid代理默认端口-漫游内网3306mysql3311/2kangle主机管理系统......
  • 从0开始机器学习--11.关联规则挖掘基础(概念-频繁项集、关联规则、支持度置信度提升度,
    写在前面“关联规则挖掘”是数据挖掘的一个重要方向。在本专栏之前的所有文章中,我们已经了解了机器学习和神经网络的基本模型、数据分析方面的应用。那这篇文章所介绍的就是在数据分析方面的另一种“关联规则”的挖掘。本博文是我个人根据ppt的学习记录稍加整理和理解,若有疑问......
  • 【预训练-微调】基于经典网络架构训练图像分类模型-学习笔记
    本学习笔记来源于B站: 迪哥128集强力打造:深度学习PyTorch从入门到实战 的第30-41个视频。在本预训练-微调代码中,重点要学习的内容包括:1、加载官方提供的经典网络架构和已经训练好的模型,对最后一层全连接层进行修改,改为适合自己任务的网络架构。在此基础上,训练最后一层全......