初阶通讯录自主实现,就算你没学过C语言也没关系,手把手教你写出来,你还在等什么??

一、通讯录简介

通讯录的实现分为三个模块:Contact.h Contact.c test.c
Contact.h:.h为后缀的文件一般放头文件的包含,结构体类型的定义,宏定义,函数的声明等等。
**Contact.c:**完成函数的实现。
**test.c:**测试通讯录功能。

二、结构体类型的定义

通讯录需要记录当前已经存放的联系人的个数以及由每一个联系人信息构成的数组,而对于任何一个联系人都是一个复杂的对象,需要用多个变量去描述这个联系人的特点,例如:需要姓名,性别,年龄等等,所以又需要一个结构体描述。,所以这里需要两个结构体嵌套使用。

#define MAX_CAPACITY 1000
//创建描述联系人信息的结构体
typedef struct peo_info
{
	char name[20];
	char sex[10];
	int age;
	char addr[20];
	char tele[12];
}peo_info;
//创建通讯录的结构体
typedef struct Contact
{
	//通讯录结构体里面嵌套一个保存联系人数据的结构体数组
	struct peo_info data[MAX_CAPACITY];
	int sz;

}Contact;

三、test.c整体测试的框架

#include "contact.h"

void menu(void)
{
	printf("*******************************\n");
	printf("****   1.add     2.del     ****\n");
	printf("****   3.search  4.mod     ****\n");
	printf("****   5.show    6.sort    ****\n");
	printf("*******     0.exit      *******\n");

}

int main()
{
	Contact con;//创建通讯录
	InitContact(&con);//初始化通讯录
	int input = 0;//对通讯录进行的操作
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			Add(&con);//添加联系人
			break;
		}
		case 2:
		{
			Del(&con);//删除联系人
			break;
		}
		case 3:
		{
			Search(&con);//查找联系人
			break;
		}
		case 4:
		{
			Mod(&con);//修改联系人
			break;
		}
		case 5:
		{
			Show(&con);//展示联系人
			break;
		}
		case 6:
		{
			Sort(&con);//排序
			break;
		}
		case 0:
		{
			printf("退出通讯录!!!\n");
			break;
		}
		default:
		{
			printf("选择错误,请重新选择!!\n");
			break;
		}
		}
	} while (input);

	return 0;
}

四、Conatct.h

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

#define MAX_CAPACITY 1000
//创建描述联系人信息的结构体
typedef struct peo_info
{
	char name[20];
	char sex[10];
	int age;
	char addr[20];
	char tele[12];
}peo_info;
//创建通讯录的结构体
typedef struct Contact
{
	struct peo_info data[MAX_CAPACITY];
	int sz;

}Contact;

//函数声明
extern void InitContact(Contact* con);
extern void Add(Contact* con);
extern void Show(Contact* con);
extern void Del(Contact* con);
extern void Search(Contact* con);
extern void Mod(Contact* con);
extern void Sort(Contact* con);

五、通讯录各个函数的实现

5.1 初始化通讯录函数的实现

其实这里很简单,就是把存放联系人的数组初始化为0就行了,已经存放的联系人sz当然也为0

void InitContact(Contact* con)
{
	assert(con);
	memset(con->data, 0, sizeof(peo_info) * MAX_CAPACITY);
	con->sz = 0;
}

5.2 添加联系人函数的实现

void Add(Contact* con)
{
	assert(con);
	if (con->sz == MAX_CAPACITY)
	{
		printf("通讯录已满,无法添加联系人!\n");
		return;
	}
	//con->data[con->sz].name的意思是通讯录con指向的data数组
	//下标为con->sz对应的peo_info结构体的name成员,由于每添加
	//一个数据con->sz就会++,所以下标刚好一一对应。
	printf("请输入添加的联系人姓名:>");
	scanf("%s", con->data[con->sz].name);
	printf("请输入添加的联系人性别:>");
	scanf("%s", con->data[con->sz].sex);	
	printf("请输入添加的联系人年龄:>");
	scanf("%d", &con->data[con->sz].age);
	printf("请输入添加的联系人电话:>");
	scanf("%s", con->data[con->sz].tele);
	printf("请输入添加的联系人地址:>");
	scanf("%s", con->data[con->sz].addr);

	con->sz++;
	printf("添加成功\n");
}

5.3 删除联系人函数的实现

int FindPeoInfo(Contact* con, char name[])
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)//遍历查找
	{
	    //返回值=0,证明通讯录里面下标为i的联系人的姓名与要查找的人的姓名
	    //一样,证明找到了,返回i下标
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;//循环遍历完都没有找到,那就是没有这个人了,返回-1代表找不到
}
void Del(Contact* con)
{
	assert(con);
	printf("请输入需要删除的联系人的名字:>");
	char name[20] = { 0 };
	scanf("%s", name);
	int pos = FindPeoInfo(con, name);//先查找指定联系人,再删除
	if (-1 == pos)
	{
		printf("要删除的联系人不存在!\n");
		return;
	}
	int i = 0;
	for (i = pos; i < con->sz - 1; i++)
	{
	    //迭代往前挪,后一个数据覆盖前一个,最终把需要删除的位置覆盖掉
		con->data[i] = con->data[i + 1];
	}
	con->sz--;//删除一个元素con->sz自然也需要--。
	printf("删除成功!\n");
}

5.4 查找联系人函数的实现

int FindPeoInfo(Contact* con, char name[])
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)//遍历查找
	{
	    //返回值=0,证明通讯录里面下标为i的联系人的姓名与要查找的人的姓名
	    //一样,证明找到了,返回i下标
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;//循环遍历完都没有找到,那就是没有这个人了,返回-1代表找不到
}
void Search(Contact* con)
{
	assert(con);
	char name[20] = { 0 };
	printf("请输入需要查找的联系人的姓名:>");
	scanf("%s", name);
	int pos = FindPeoInfo(con,name);
	if (pos == -1)
	{
		printf("找不到此联系人!\n");
		return;
	}
	//找到了就按照格式打印出来,‘\t’是制表符
	printf("%-20s\t%-10s\t%-5s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	
	printf("%-20s\t%-10s\t%-5d\t%-12s\t%-20s\n", con->data[pos].name,
		con->data[pos].sex,
		con->data[pos].age,
		con->data[pos].tele,
		con->data[pos].addr);
	
}

5.5 改变联系人信息函数的实现

int FindPeoInfo(Contact* con, char name[])
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)//遍历查找
	{
	    //返回值=0,证明通讯录里面下标为i的联系人的姓名与要查找的人的姓名
	    //一样,证明找到了,返回i下标
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;//循环遍历完都没有找到,那就是没有这个人了,返回-1代表找不到
}
void Mod(Contact* con)
{
	assert(con);
	printf("请输入需要修改的联系人的姓名:>");
	char name[20] = { 0 };
	scanf("%s", name);
	int pos = FindPeoInfo(con, name);
	if (pos == -1)
	{
		printf("该联系人不存在!\n");
		return;
	}
	//下面其实就是跟添加联系人信息是一样的逻辑的
	//修改其实也是输入信息嘛
	printf("请输入新的联系人姓名:>");
	scanf("%s", con->data[pos].name);
	printf("请输入添加的联系人性别:>");
	scanf("%s", con->data[pos].sex);
	printf("请输入添加的联系人年龄:>");
	scanf("%d", &con->data[pos].age);
	printf("请输入添加的联系人电话:>");
	scanf("%s", con->data[pos].tele);
	printf("请输入添加的联系人地址:>");
	scanf("%s", con->data[pos].addr);

	printf("修改成功!\n");

}

5.6 展示联系人信息函数的实现

void Show(Contact* con)
{
	assert(con);
	int i = 0;
	//先打印表头,可以更加美观
	printf("%-20s\t%-10s\t%-5s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	//遍历格式化打印各个联系人信息就好咯
	for (i = 0; i < con->sz; i++)
	{
		printf("%-20s\t%-10s\t%-5d\t%-12s\t%-20s\n", con->data[i].name,
													 con->data[i].sex, 
													 con->data[i].age, 
													 con->data[i].tele, 
													 con->data[i].addr);
	}
}

5.7 通讯录排序函数的实现

这里大家可以用不同的方式对联系人进行排序,我这里就用qsort对联系人的名字进行的排序

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((peo_info*)e1)->name, ((peo_info*)e2)->name);
}

void Sort(Contact* con)
{
	assert(con);
	qsort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);
	printf("排序成功\n");
	Show(con);


}

另外一种写法是我自主实现一个通用的冒泡排序

void swap(char* buf1, char* buf2, size_t width)
{
	size_t i = 0;
	//数组中的每一个数据占width个字节,我们只需要循环width次就能把两个元素的每一个字节都交换,
	//最终两个元素的内容也就交换成功了
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}

}

void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	//这里是典型的冒泡排序的方法
	size_t i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//这里的cmp函数就是就是比较相邻的两个元素的大小,如果返回值大于0,则证明前一个元素
			//比后一个元素大,则需要交换这两个元素。由于这里的base指针的类型是void*,所以我们
			//首先需要将它强制类型转换成char*类型的指针,那为什么是转换成char*而不是int*, 
			//double*呢?其实很简单,你试想一下,我们比较完了两个相邻的元素之后是不是需要
			//拿后一个和这两个元素中大的元素进行比较大小,但是大家别忘了,指针类型的大小可是决 
			//定了你指针加1跳过几个字节的啊,整形指针加1跳过一个整形,字符指针加1跳过一个字节
			//但是我们并不知道将来这个函数会被用来排序什么类型的数据的啊,但是无论是数目类型的 
			//数据,它的大小都至少为1个字节吧,所以转换成(char*)类型是最合理的。而参数中的宽 
			//度又正好能让我们找到下一个元素,只需要再起始地址加上宽度*j就能找到下一个元素了
			//所以if语句里面的判断条件应该这样写

			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}

}


int cmp_by_name(const void* e1, const void* e2)
{
	//通过姓名对结构体进行排序,需要用到strcmp函数,依然是返回1,0,-1
	return strcmp(((peo_info*)e1)->name, ((peo_info*)e2)->name);
}

void Sort(Contact* con)
{
	assert(con);
	//qsort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);
	bubble_sort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);
	printf("排序成功\n");
	Show(con);


}

当然,这个排序的方法的性能肯定比不上qsort本来的性能的,所以这个只是作为一个通用的排序的方法的参考。

六、通讯录完整代码汇总

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

void menu(void)
{
	printf("*******************************\n");
	printf("****   1.add     2.del     ****\n");
	printf("****   3.search  4.mod     ****\n");
	printf("****   5.show    6.sort    ****\n");
	printf("*******     0.exit      *******\n");

}

int main()
{
	Contact con;
	InitContact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请输入:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			Add(&con);
			break;
		}
		case 2:
		{
			Del(&con);
			break;
		}
		case 3:
		{
			Search(&con);
			break;
		}
		case 4:
		{
			Mod(&con);
			break;
		}
		case 5:
		{
			Show(&con);
			break;
		}
		case 6:
		{
			Sort(&con);
			break;
		}
		case 0:
		{
			printf("退出通讯录!!!\n");
			break;
		}
		default:
		{
			printf("选择错误,请重新选择!!\n");
			break;
		}
		}
	} while (input);

	return 0;
}

contact.h

#pragma once

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

#define MAX_CAPACITY 1000

typedef struct peo_info
{
	char name[20];
	char sex[10];
	int age;
	char addr[20];
	char tele[12];
}peo_info;

typedef struct Contact
{
	struct peo_info data[MAX_CAPACITY];
	int sz;

}Contact;

//函数声明
extern void InitContact(Contact* con);
extern void Add(Contact* con);
extern void Show(Contact* con);
extern void Del(Contact* con);
extern void Search(Contact* con);
extern void Mod(Contact* con);
extern void Sort(Contact* con);

contact.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

void InitContact(Contact* con)
{
	assert(con);
	memset(con->data, 0, sizeof(peo_info) * MAX_CAPACITY);
	con->sz = 0;
}


void Add(Contact* con)
{
	assert(con);
	if (con->sz == MAX_CAPACITY)
	{
		printf("通讯录已满,无法添加联系人!\n");
		return;
	}
	printf("请输入添加的联系人姓名:>");
	scanf("%s", con->data[con->sz].name);
	printf("请输入添加的联系人性别:>");
	scanf("%s", con->data[con->sz].sex);	
	printf("请输入添加的联系人年龄:>");
	scanf("%d", &con->data[con->sz].age);
	printf("请输入添加的联系人电话:>");
	scanf("%s", con->data[con->sz].tele);
	printf("请输入添加的联系人地址:>");
	scanf("%s", con->data[con->sz].addr);

	con->sz++;
	printf("添加成功\n");
}

void Show(Contact* con)
{
	assert(con);
	int i = 0;
	printf("%-20s\t%-10s\t%-5s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	for (i = 0; i < con->sz; i++)
	{
		printf("%-20s\t%-10s\t%-5d\t%-12s\t%-20s\n", con->data[i].name,
													 con->data[i].sex, 
													 con->data[i].age, 
													 con->data[i].tele, 
													 con->data[i].addr);
	}
}

int FindPeoInfo(Contact* con, char name[])
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

void Del(Contact* con)
{
	assert(con);
	printf("请输入需要删除的联系人的名字:>");
	char name[20] = { 0 };
	scanf("%s", name);
	int pos = FindPeoInfo(con, name);
	if (-1 == pos)
	{
		printf("要删除的联系人不存在!\n");
		return;
	}
	int i = 0;
	for (i = pos; i < con->sz - 1; i++)
	{
		con->data[i] = con->data[i + 1];
	}
	con->sz--;
	printf("删除成功!\n");
}

void Search(Contact* con)
{
	assert(con);
	char name[20] = { 0 };
	printf("请输入需要查找的联系人的姓名:>");
	scanf("%s", name);
	int pos = FindPeoInfo(con,name);
	if (pos == -1)
	{
		printf("找不到此联系人!\n");
		return;
	}
	printf("%-20s\t%-10s\t%-5s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	
	printf("%-20s\t%-10s\t%-5d\t%-12s\t%-20s\n", con->data[pos].name,
		con->data[pos].sex,
		con->data[pos].age,
		con->data[pos].tele,
		con->data[pos].addr);
	
}

void Mod(Contact* con)
{
	assert(con);
	printf("请输入需要修改的联系人的姓名:>");
	char name[20] = { 0 };
	scanf("%s", name);
	int pos = FindPeoInfo(con, name);
	if (pos == -1)
	{
		printf("该联系人不存在!\n");
		return;
	}
	printf("请输入新的联系人姓名:>");
	scanf("%s", con->data[pos].name);
	printf("请输入添加的联系人性别:>");
	scanf("%s", con->data[pos].sex);
	printf("请输入添加的联系人年龄:>");
	scanf("%d", &con->data[pos].age);
	printf("请输入添加的联系人电话:>");
	scanf("%s", con->data[pos].tele);
	printf("请输入添加的联系人地址:>");
	scanf("%s", con->data[pos].addr);

	printf("修改成功!\n");

}

void swap(char* buf1, char* buf2, size_t width)
{
	size_t i = 0;
	//数组中的每一个数据占width个字节,我们只需要循环width次就能把两个元素的每一个字节都交换,
	//最终两个元素的内容也就交换成功了
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}

}

void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	//这里是典型的冒泡排序的方法
	size_t i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//这里的cmp函数就是就是比较相邻的两个元素的大小,如果返回值大于0,则证明前一个元素
			//比后一个元素大,则需要交换这两个元素。由于这里的base指针的类型是void*,所以我们
			//首先需要将它强制类型转换成char*类型的指针,那为什么是转换成char*而不是int*, 
			//double*呢?其实很简单,你试想一下,我们比较完了两个相邻的元素之后是不是需要
			//拿后一个和这两个元素中大的元素进行比较大小,但是大家别忘了,指针类型的大小可是决 
			//定了你指针加1跳过几个字节的啊,整形指针加1跳过一个整形,字符指针加1跳过一个字节
			//但是我们并不知道将来这个函数会被用来排序什么类型的数据的啊,但是无论是数目类型的 
			//数据,它的大小都至少为1个字节吧,所以转换成(char*)类型是最合理的。而参数中的宽 
			//度又正好能让我们找到下一个元素,只需要再起始地址加上宽度*j就能找到下一个元素了
			//所以if语句里面的判断条件应该这样写

			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}

}


int cmp_by_name(const void* e1, const void* e2)
{
	//通过姓名对结构体进行排序,需要用到strcmp函数,依然是返回1,0,-1
	return strcmp(((peo_info*)e1)->name, ((peo_info*)e2)->name);
}

void Sort(Contact* con)
{
	assert(con);
	//qsort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);
	bubble_sort(con->data, con->sz, sizeof(con->data[0]), cmp_by_name);
	printf("排序成功\n");
	Show(con);


}




七、总结

其实这个初阶的通讯录实现起来或许没有你想象的那么简单,但是也没有你想象的那么难,就是先把整个测试的架构建起来,然后就逐一实现每一个函数需要的功能就可以了,初阶的通讯录的分享就到这里,你学会了吗?后续将会出进阶的能够动态开辟空间的通讯录的实现,想看的记得点点关注哦!如果这篇文章对你有帮助,请动动你发财的小手点点赞呗!我们下期见。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
如果你想学习C语言,即使你有计算机编程的基础,也不必担心。有一些简单的步骤可以帮助你入门。首先,你可以从了解C语言的基础知识开始。有一些介绍C语言基础知识的资料可以帮助你大致了解C语言的特点和用法。接下来,你需要选择一个合适的开发环境来编和运行C语言程序。一个常见的选择是使用编译器,例如Dev C++或者Visual Studio 2019。然后,你可以尝试编你的第一个C语言程序。这个程序可以非常简单,比如打印一句话在屏幕上。通过编和运行这个程序,你可以开始对C语言的语法和结构有一些实际的了解[3]。从这个起点开始,你可以继续学习更多的C语言知识,并挑战更复杂的编程任务。在学习过程中,你可以阅读材、参考文档、查找在线资源或参加编程课程来加深你的理解和技能。记住,坚持实践是学习编程的关键。通过不断地编和调试程序,你会逐渐掌握C语言的基础,并逐渐提升自己的编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [C语言初阶——手把手零基础/新手入门(万字心得笔记)](https://blog.csdn.net/m0_63312733/article/details/122145605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值