使用C语言实现简单通讯录

C语言通讯录

开头

这是一篇介绍如何使用C语言实现一个简单通讯录的博客, 我使用的编译器是vs2022.

同时, 我是一名C语言的初学者, 目前学了半年还比较菜, 如果有写不好的地方,希望大佬能够见谅和指点一下. 如果你跟我一样也是初学者, 那希望这篇文章对你有帮助.

程序设计的目标

用c语言实现一个能够存储1000个人的姓名,电话,地址,年龄的通讯录, 并且能够实现以下功能

0,退出程序

1,增加一个人的信息

2,删除指定人信息

3,搜索指定人并显示其信息

4,修改指定人信息

5,按照年龄大小对通讯录进行排序

程序实现的思路

我将程序分为3个文件, 头文件(contact.h)用于声明函数和结构体变量等, 源文件(contact.c)用于实现通讯录增删改查等功能的函数, 源文件(test.c)用于实现程序的整体执行.

test.c

先搭个整体框架. 首先在main()中创建一个结构体变量con(该结构体定义在contact.h)和整型input并初始化为0, 由于该代码一定会执行一次所以我用do…while语句, 利用switch语句来进行选项的选择–选项我使用枚举类型主要是增加可读性. 然后在依次在选项后面添加上我们实现的增删改查等函数.

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"


void menu()
{
	printf("******************************\n");
	printf("********1.增加  2.删除*********\n");
	printf("********3.查找  4.修改*********\n");
	printf("********5.打印  6.排序*********\n");
	printf("********0.退出****************\n");
}


enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIF,
	PRINT,
	SORT
};

int main()
{
	Contact con = { 0 };
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			contact_add(&con);
			break;
		case DEL:
			contact_del(&con);
			break;
		case SEARCH:
			contact_sear(&con);
			break;
		case MODIF:
			contact_modi(&con);
			break;
		case PRINT:
			contact_print(&con);
			break;
		case SORT:
			contact_sort(&con);
			break;
		case EXIT:
			printf("已退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}

contact.h

在头文件中定义最大长度的宏以及结构体PeopleInfo用于存放每个的信息,结构体Contact则存放1000个人的信息,同时利用sz统计实际人数.

对于这两个结构体我都使用了typedef进行重命名,主要是为了方便使用.

#pragma once

#include<stdio.h>
#include<string.h>
#include<stdlib.h>


#define NAME_MAX 20  // 名字
#define NUM_MAX 15  // 电话
#define ADDR_MAX 20  // 地址
#define MAX 1000  // 人数


typedef struct PeopleInfo  // 单个人的信息
{
	char name[NAME_MAX];
	char number[NUM_MAX];
	char adderss[ADDR_MAX];
	int age;
}PeopleInfo;


typedef struct Contact  
{
	PeopleInfo data[MAX];
	int sz;  // 记录人数
}Contact;


void contact_add(Contact* pc);  // 增加一个人的信息

void contact_print(Contact* pc);  // 打印信息

void contact_del(Contact* pc);  // 删除指定人

void contact_sear(Contact* pc);  // 查询指定人并打印其信息

void contact_modi(Contact* pc);  // 修改指定人信息

void contact_sort(Contact* pc); // 按照年龄进行排序


contact.c

这一个源文件才是重头戏. 这里我将给出我设计时的一些思路, 希望能够帮助读者理解这个源文件.

void contact_add(Contact* pc) – 增加一个人的信息

首先实现的就是向通讯录增加一个人信息的函数, 这个函数比较简单, 它的输入是结构体指针(&con), 然后配合上一段printf和scanf语句完成信息的存储, 以及增加后实际人数(sz)+1和打印增加成功的提示.

void contact_add(Contact* pc)
{
	if (pc->sz == MAX - 1)
	{
		printf("增加失败,通讯录已满\n");
	}
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入电话号码:>");
	scanf("%s", pc->data[pc->sz].number);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].adderss);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));  // 注意年龄是整数,要取地址
	pc->sz++;
	printf("增加成功\n");
}

但是需要注意溢出的情况, 所以我在函数的前头加上了判读语句.

sz代表下标, data[MAX] 的最大下标就是MAX - 1

void contact_print(Contact* pc) – 打印信息

完成了增加函数, 我们需要确认是否真的增加了一个人的信息, 所以第二步就是设计打印的函数. 因为要打印结构体数组和打印人数, 所以该函数中输入是结构体指针(&con), 使用for循环来打印实际人数(sz).

void contact_print(Contact* pc)
{
	int i = 0;
	printf("%-10s %-10s %-10s %-10s\n", "名字", "号码", "地址", "年龄");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-10s %-10s %-10s %-10d\n",pc->data[i].name,
											pc->data[i].number,
											pc->data[i].adderss,
											pc->data[i].age);  // 注意最后打印的是%d
	}
}

printf("%-10s %-10s %-10s %-10s\n", "名字", "号码", "地址", "年龄");将字符串这样打印主要是为了格式统一, 就是好看点

请注意, 每次增加人后sz都会+1, 也就是说sz下标处是初始化状态(啥都没有), 所以也就不需要打印sz下标对应的值了.

到这里, 程序执行后可以获得这样的结果

在这里插入图片描述

这里*对不齐应该是我字体的原因(强迫症犯了)

void contact_del(Contact* pc) – 删除指定人

接下了实现的是删除函数, 它需要用户输入名字来找到要删除的特定人, 由于后续删改查都需要根据找到特定人, 所以在这里再设计一个函数int find_someone_by_name(Contact* pc)

int find_someone_by_name(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	int i = 0;
	printf("请输入要找人的名字:>");
	scanf("%s", name);
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;  // 返回下标
		}
	}
	return -1;
}

该函数要求用户输入名字并将其储存于name数组中, 然后遍历数组判断输入名字是否存在于结构体, 如果存在就返回i(此时i代表的就是要找的人在结构体的下标), 如果不存在则返回1.

好了, 实现这个函数后接着完成我们的删除函数. 首先, 创建一个临时变量ret用来接收寻找函数的返回值, 如果ret == -1 那就说明没找到这个人, 打印找不到的信息提醒用户, 如果ret != -1 那么进行删除动作. 由于删除完成后会留下一个空数组, 影响打印时的美观, 所以我们直接把删除的那个人后面的数据往前移动一个, 也就是将该人后续数据前移, 直接覆盖这个人. 然后记得在删除后实际人数会减1(sz–), 同时可以加上一个判断条件防止通讯录为空的情况–当然也可以不用,因为通讯录为空时你就不可能找到人.

void contact_del(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("删除失败,电话簿为空\n");
	}
	int ret = find_someone_by_name(pc);
	if (ret == -1)
	{
		printf("没有找到这个人\n");
		return;  // 结束函数
	}
	else
	{
		int i = 0;
		for (i = ret; i < pc->sz; i++);
		{
			pc->data[i] = pc->data[i + 1];  // 从获取到的下标开始覆盖前一个数据
		}
		pc->sz--;  // 人数减少
		printf("删除成功\n");
	}
	return;
}

结构体变量之间是可以直接用"="赋值的.

void contact_sear(Contact* pc) – 查询指定人并打印其信息

接下来实现查找这个功能, 同样这个函数也需要找到特定人, 所以继续使用int find_someone_by_name(Contact* pc)这个函数. 首先创建变量ret来储存返回值, 如果ret == -1 那就说明没找到这个人, 打印找不到的信息提醒用户, 如果ret != -1 那么进行打印动作. 打印的功能可以直接拷贝contact_print函数, 但需要注意这里只打印一个人, 它的下标为ret.

void contact_sear(Contact* pc)
{
	int ret = find_someone_by_name(pc);
	if (ret == -1)
	{
		printf("没有找到这个人\n");
		return;  // 结束函数
	}
	else  // 找到时打印这个人的信息
	{
		printf("%-10s %-10s %-10s %-10s\n", "名字", "号码", "地址", "年龄");
		printf("%-10s %-10s %-10s %-10d\n", pc->data[ret].name,
											pc->data[ret].number,
											pc->data[ret].adderss,
											pc->data[ret].age);  // 注意最后打印的是%d
	}
	return;
}
void contact_modi(Contact* pc) – 修改指定人信息

既然是修改指定人的信息, 那也同样需要int find_someone_by_name(Contact* pc)这个函数, 继续我们的老方法. 创建变量ret来储存返回值, 如果ret == -1 那就说明没找到这个人, 打印找不到的信息提醒用户, 如果ret != -1 那么进行修改动作. 修改动作可以直接拷贝contact_add函数, 但需要注意这里只修改一个人, 它的下标为ret.

void contact_modi(Contact* pc)
{
	int ret = find_someone_by_name(pc);
	if (ret == -1)
	{
		printf("没有找到这个人\n");
		return;  // 结束函数
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入电话号码:>");
		scanf("%s", pc->data[ret].number);
		printf("请输入地址:>");
		scanf("%s", pc->data[ret].adderss);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("修改成功\n");  // 注意这里人数无变化所以sz不用+1
	}
	return;
}
void contact_sort(Contact* pc) – 按照年龄进行排序

这是最后一个函数, 它的功能是根据年龄大小对结构体数组data[]进行排序(从小到大). 要实现这个函数我能可以借助一个库函数qsort在这里插入图片描述

这个函数有4个参数, 第一个参数即我们要排序的目标(data), 第二个即排序对象的个数(sz), 第三个即每个元素的长度–大小为字节(sizeof(data[0])), 第四个是一个函数指针, 这里使用的是回调函数, 关于函数指针和回调函数我这里就不展开了, 反正这里就是让你设计一个函数, 它可以比较两个数之间的大小–大于返回>0|小于返回<0|等于返回0.

所以我们先实现比较函数

// 比较函数,用于qsor函数中
int cmp(const void* a, const void* b)
{
	PeopleInfo* p1 = (PeopleInfo*)a;
	PeopleInfo* p2 = (PeopleInfo*)b;
	return p1->age - p2->age;
}

因为传入的指针内容不需要改变, 所以加const更加安全. 使用void*是为了通用性, 只要你想任何同类型的两数都可以比较, 当然在你知道你要比较的两个数是啥类型时, 你也可以直接写那个类型, 例如PeopleInfo* a. 这里我使用的是void*所以还要进行强制类型转换, 这样编译器才知道这两值之间的间隔, 进而可以计算. 计算就非常简单粗暴, 直接返回两数相减的结果, 当然你也可以使用if…else返回1, -1, 0. 如果你想实现反序, 直接在return的值前加上"-", 或者p1和p2位置互换一下即可.

void 类型是不能进行±等运算的

实现了比较函数, 接下来就是实现我们的contact_sort函数了

void contact_sort(Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp);
	contact_print(pc);
	printf("排序完成\n");
}

注意这里第二个参数是sz, 而不能是MAX. 如果是MAX则会把初始化为0的排在前头, 造成错误.

结语

这个通讯录还有许多可以改进的地方, 比如排序的方式, 修改项的数量等等, 在这里只是给出一些基本的功能, 剩下就交给你自己吧. 总的来说, 这个程序难度并不大, 只是有些麻烦. 同时这也是我第一次写博客, 只能很得劲,不知不觉码了3000个字左右, 有一点小累, 如果你有什么问题或者建议请在评论区给出, 如果觉得对你有帮助也请点个赞让我开心开心o( ̄▽ ̄)ブ.
.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值