【通讯录----C语言实现】

本文档详细介绍了使用C语言实现的通讯录程序,包含动态内存分配、文件操作功能。通过main函数调用,用户可以添加、删除、查找、修改联系人信息,并支持通讯录排序。关键功能包括内存动态扩展、通讯录内容持久化存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

请添加图片描述



前言

个人写的通讯录C语言实现,望交流


版本①

一、main函数所在源文件:test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game0122.h"

// 实现一个通讯录:(不考虑相同名字)
// 通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
// 提供方法:
// 1-添加联系人信息
// 2-删除指定联系人信息
// 3-查找指定联系人信息
// 4-修改指定联系人信息
// 5-显示所有联系人信息
// 6-清空所有联系人
// 7-以名字排序所有联系人

void Meun()
{
	printf("--------------------------------------\n");
	printf("----------0-退出----------------------\n");
	printf("----------1-添加联系人信息------------\n");
	printf("----------2-删除指定联系人信息--------\n");
	printf("----------3-查找指定联系人信息--------\n");
	printf("----------4-修改指定联系人信息--------\n");
	printf("----------5-显示所有联系人信息--------\n");
	printf("----------6-清空所有联系人------------\n");
	printf("----------7-以名字排序所有联系人------\n");
	printf("--------------------------------------\n");
}



int main()
{
	// 创建一个数组,实际大小为1000的数组,如果直接将其作为通讯录,
	// 则通讯录中已有多少数据是未知的,后续增删查改打印都无法实现
	// list data[1000] = { 0 };

	// 创建通讯录
	contact con = { 0 };

	// 初始化通讯录(也为之后的优化做铺垫)
	Init(&con);

	int input = 0;
	do {
		Meun();
		printf("请输入您的选项\n");
		scanf("%d", &input);

		switch (input)
		{
		case 0:
			printf("已退出/n");
			break;
		case 1:
			// 传入结构体地址
			Add(&con);
			break;
		case 2:
			Del(&con);
			break;
		case 3:
			Find(&con);
			break;
		case 4:
			Amd(&con);
			break;
		case 5:
			Print(&con);
			break;
		case 6:
			Emp(&con);
			break;
		case 7:
			Sort(&con);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);

	return 0;
}

二、相关函数定义源文件:game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game0122.h"

// 初始化通讯录
void Init(contact* p)
{
	assert(p);
	(p->sz) = 0;
	memset(p->data, 0, sizeof(p->data));
}


// 判断通讯录是否满
int Is_Full(contact* p)
{
	assert(p);
	if ((p->sz) < MAX) {
		return 1;
	}
	else {
		return 0;
	}
}


// 查询通讯录联系人
int Search(const contact* p, char name[]) //const保护通讯录不被改变
{
	int i = 0;
	for (i = 0; i < (p->sz); i++) {
		if (0 == strcmp(name, (p->data[i]).name)) {
			// 查询成功返回下标
			return i;
		}
	}
	// 循环结束仍未返回说明查询失败,返回-1
	return -1;
}


// 1-添加联系人信息
void Add(contact* p)
{
	assert(p);
	// 判断通讯录是否存满
	if (Is_Full(p)) {
		printf("请输入您要添加的联系人信息:姓名、性别、年龄、电话、住址\n");

		// 注:小心在输入age为int类型时,须带&,其余类似name本身就为数组名,是地址
		scanf("%s %s %d %s %s", (p->data[(p->sz)]).name, (p->data[(p->sz)]).sex, \
			& (p->data[(p->sz)]).age, (p->data[(p->sz)]).call, (p->data[(p->sz)]).address);

		// 有效联系人数量加一并提示成功
		(p->sz)++;
		printf("增加联系人成功\n");
	}
	else {
		printf("通讯录已满,无法继续添加\n");
	}

}


// 2-删除指定联系人信息
void Del(contact* p)
{
	assert(p);
	// 输入需要删除第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后删除
	/*int num = 0;
	printf("请输入您需要删除的联系人序号(从0开始)\n");
	scanf("%d", &num);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再删除
	char name[NAMEMAX] = { 0 };
	printf("请输入您要删除的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int ret = Search(p, name);
	if (-1 == ret) {
		printf("未查询到此人\n");
	}
	else {
		printf("删除成功\n");

		// 将该位置覆盖来删除
		int i = 0;
		/*for (i = ret; i < ((p->sz) - 1); i++) {
			strcpy((p->data[i]).name, (p->data[i + 1]).name);
			strcpy((p->data[i]).sex, (p->data[i + 1]).sex);
			(p->data[i]).age = (p->data[i + 1]).age;
			strcpy((p->data[i]).call, (p->data[i + 1]).call);
			strcpy((p->data[i]).address, (p->data[i + 1]).address);
		}*/
		// 结构体变量很规律,可以相互赋值:(优化版本)
		for (i = 0; i < (p->sz) - 1; i++) {
			(p->data[i]) = (p->data[i + 1]);
		}

		// 有效联系人数量减一
		(p->sz)--;
	}
}


// 3 - 查找指定联系人信息
void Find(contact* p)
{
	assert(p);
	// 输入需要查找第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后打印
	/*int i = 0;
	printf("请输入您需要查找的联系人序号(从0开始)\n");
	scanf("%d", &i);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再打印
	char name[NAMEMAX] = { 0 };
	printf("请输入您要查找的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int i = Search(p, name);
	if (-1 == i) {
		printf("未查询到此人\n");
	}
	else {
		printf("查询成功\n");
		printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			(p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
	}
}


// 4 - 修改指定联系人信息
void Amd(contact* p)
{
	assert(p);
	// 判断通讯录是否为空,如果为空就无法删除并直接返回
	if (0 == (p->sz)) {
		printf("通讯录为空,无法删除\n");
		return;
	}

	// 输入需要修改第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后修改
	/*int i = 0;
	printf("请输入您需要修改的联系人序号(从0开始)\n");
	scanf("%d", &i);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再修改
	char name[NAMEMAX] = { 0 };
	printf("请输入您要修改的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int i = Search(p, name);
	if (-1 == i) {
		printf("未查询到此人\n");
	}
	else {
		// 输入需要修改后的内容
		printf("请输入你所修改后的信息:姓名、性别、年龄、电话、住址\n");
		scanf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			& (p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
		printf("修改完毕\n");
	}
}


// 5-显示所有联系人信息
void Print(const contact* p) //const保护通讯录不被改变
{
	assert(p);
	// 按需求,也可以分开提示并输入
	printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "住址");

	int i = 0;
	for (i = 0; i < (p->sz); i++) {
		printf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			(p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
	}
}


// 6-清空所有联系人
void Emp(contact* p)
{
	assert(p);
	// 直接将有效联系人数量置为0
	(p->sz) = 0;
}


// 7-以名字排序所有联系人
void Sort(contact* p)
{
	assert(p);
	// 冒泡排序
	int i = 0;
	int j = 0;
	for (i = 0; i < (p->sz) - 1; i++) {
		for (j = 0; j < (p->sz) - 1 - i; j++) {
			if (strcmp((p->data[j]).name, (p->data[j + 1]).name) > 0) {
				list tmp = p->data[j];
				p->data[j] = p->data[j + 1];
				p->data[j + 1] = tmp;
			}
		}
	}
}

三、头文件、宏定义、函数等声明头文件:game.h

#pragma once

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

// 注:需有意识将变动的常量值用会宏定义
#define MAX 1000
#define NAMEMAX 10
#define SEXMAX 5
#define CALLMAX 10
#define ADDRESSMAX 10


// 在头文件声明一个人物信息结构体类型
typedef struct List
{
	char name[NAMEMAX];
	char sex[SEXMAX];
	int age;
	char call[CALLMAX];
	char address[ADDRESSMAX];
}list;


// 再声明一个真正的通讯录结构体类型
typedef struct Contact
{
	// 可存1000个人的人物信息结构体数组
	list data[MAX];
	// 通讯录中的已存有效信息的数量
	int sz;
}contact;


// 初始化通讯录
void Init(contact* p);

// 判断通讯录是否满
int Is_Full(contact* p);

// 查询通讯录联系人
int Search(contact* p, char name[]);

// 1-添加联系人信息
void Add(contact* p);

// 2-删除指定联系人信息
void Del(contact* p);

// 3 - 查找指定联系人信息
void Find(contact* p);

// 4 - 修改指定联系人信息
void Amd(contact* p);

// 5-显示所有联系人信息
void Print(const contact* p);

// 6-清空所有联系人
void Emp(contact* p);

// 7-以名字排序所有联系人
void Sort(contact* p);

版本②(附加动态内存开辟效果)

一、main函数所在源文件:test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game0122.h"

// 实现一个通讯录:(不考虑相同名字)
// 通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
// 提供方法:
// 1-添加联系人信息
// 2-删除指定联系人信息
// 3-查找指定联系人信息
// 4-修改指定联系人信息
// 5-显示所有联系人信息
// 6-清空所有联系人
// 7-以名字排序所有联系人


// 实现一个枚举,使得switch代码的case分支更有意义,之前学过case后可以写枚举类型
enum Option
{
	EXIT,//0
	ADD,
	DEL,
	FIND,
	AMD,
	PRINT,
	EMP,
	SORT
};


void Meun()
{
	printf("--------------------------------------\n");
	printf("----------0-退出----------------------\n");
	printf("----------1-添加联系人信息------------\n");
	printf("----------2-删除指定联系人信息--------\n");
	printf("----------3-查找指定联系人信息--------\n");
	printf("----------4-修改指定联系人信息--------\n");
	printf("----------5-显示所有联系人信息--------\n");
	printf("----------6-清空所有联系人------------\n");
	printf("----------7-以名字排序所有联系人------\n");
	printf("--------------------------------------\n");
}



int main()
{
	// 创建一个数组,实际大小为1000的数组,如果直接将其作为通讯录,
	// 则通讯录中已有多少数据是未知的,后续增删查改打印都无法实现
	// list data[1000] = { 0 };

	// 创建通讯录
	contact con = { 0 };

	// 初始化通讯录(也为之后的优化做铺垫)
	Init(&con);

	int input = 0;
	do {
		Meun();
		printf("请输入您的选项\n");
		scanf("%d", &input);

		switch (input)
		{
		case EXIT:
			printf("已退出/n");
			break;
		case ADD:
			// 传入结构体地址
			Add(&con);
			break;
		case DEL:
			Del(&con);
			break;
		case FIND:
			Find(&con);
			break;
		case AMD:
			Amd(&con);
			break;
		case PRINT:
			Print(&con);
			break;
		case EMP:
			Emp(&con);
			break;
		case SORT:
			Sort(&con);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);

	return 0;
}

二、相关函数定义源文件:game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game0122.h"

// 初始化通讯录
void Init(contact* p)
{
	assert(p);
	(p->sz) = 0;
	memset(p->data, 0, sizeof(p->data));
}


// 判断通讯录是否满
int Is_Full(contact* p)
{
	assert(p);
	if ((p->sz) < MAX) {
		return 1;
	}
	else {
		return 0;
	}
}


// 查询通讯录联系人
int Search(const contact* p, char name[]) //const保护通讯录不被改变
{
	int i = 0;
	for (i = 0; i < (p->sz); i++) {
		if (0 == strcmp(name, (p->data[i]).name)) {
			// 查询成功返回下标
			return i;
		}
	}
	// 循环结束仍未返回说明查询失败,返回-1
	return -1;
}


// 1-添加联系人信息
void Add(contact* p)
{
	assert(p);
	// 判断通讯录是否存满
	if (Is_Full(p)) {
		printf("请输入您要添加的联系人信息:姓名、性别、年龄、电话、住址\n");

		// 注:小心在输入age为int类型时,须带&,其余类似name本身就为数组名,是地址
		scanf("%s %s %d %s %s", (p->data[(p->sz)]).name, (p->data[(p->sz)]).sex, \
			& (p->data[(p->sz)]).age, (p->data[(p->sz)]).call, (p->data[(p->sz)]).address);

		// 有效联系人数量加一并提示成功
		(p->sz)++;
		printf("增加联系人成功\n");
	}
	else {
		printf("通讯录已满,无法继续添加\n");
	}

}


// 2-删除指定联系人信息
void Del(contact* p)
{
	assert(p);
	// 输入需要删除第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后删除
	/*int num = 0;
	printf("请输入您需要删除的联系人序号(从0开始)\n");
	scanf("%d", &num);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再删除
	char name[NAMEMAX] = { 0 };
	printf("请输入您要删除的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int ret = Search(p, name);
	if (-1 == ret) {
		printf("未查询到此人\n");
	}
	else {
		printf("删除成功\n");

		// 将该位置覆盖来删除
		int i = 0;
		/*for (i = ret; i < ((p->sz) - 1); i++) {
			strcpy((p->data[i]).name, (p->data[i + 1]).name);
			strcpy((p->data[i]).sex, (p->data[i + 1]).sex);
			(p->data[i]).age = (p->data[i + 1]).age;
			strcpy((p->data[i]).call, (p->data[i + 1]).call);
			strcpy((p->data[i]).address, (p->data[i + 1]).address);
		}*/
		// 结构体变量很规律,可以相互赋值:(优化版本)
		for (i = 0; i < (p->sz) - 1; i++) {
			(p->data[i]) = (p->data[i + 1]);
		}

		// 有效联系人数量减一
		(p->sz)--;
	}
}


// 3 - 查找指定联系人信息
void Find(contact* p)
{
	assert(p);
	// 输入需要查找第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后打印
	/*int i = 0;
	printf("请输入您需要查找的联系人序号(从0开始)\n");
	scanf("%d", &i);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再打印
	char name[NAMEMAX] = { 0 };
	printf("请输入您要查找的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int i = Search(p, name);
	if (-1 == i) {
		printf("未查询到此人\n");
	}
	else {
		printf("查询成功\n");
		printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			(p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
	}
}


// 4 - 修改指定联系人信息
void Amd(contact* p)
{
	assert(p);
	// 判断通讯录是否为空,如果为空就无法删除并直接返回
	if (0 == (p->sz)) {
		printf("通讯录为空,无法删除\n");
		return;
	}

	// 输入需要修改第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后修改
	/*int i = 0;
	printf("请输入您需要修改的联系人序号(从0开始)\n");
	scanf("%d", &i);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再修改
	char name[NAMEMAX] = { 0 };
	printf("请输入您要修改的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int i = Search(p, name);
	if (-1 == i) {
		printf("未查询到此人\n");
	}
	else {
		// 输入需要修改后的内容
		printf("请输入你所修改后的信息:姓名、性别、年龄、电话、住址\n");
		scanf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			& (p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
		printf("修改完毕\n");
	}
}


// 5-显示所有联系人信息
void Print(const contact* p) //const保护通讯录不被改变
{
	assert(p);
	// 按需求,也可以分开提示并输入
	printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "住址");

	int i = 0;
	for (i = 0; i < (p->sz); i++) {
		printf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			(p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
	}
}


// 6-清空所有联系人
void Emp(contact* p)
{
	assert(p);
	// 直接将有效联系人数量置为0
	(p->sz) = 0;
}


// 7-以名字排序所有联系人
void Sort(contact* p)
{
	assert(p);
	// 冒泡排序
	int i = 0;
	int j = 0;
	for (i = 0; i < (p->sz) - 1; i++) {
		for (j = 0; j < (p->sz) - 1 - i; j++) {
			if (strcmp((p->data[j]).name, (p->data[j + 1]).name) > 0) {
				list tmp = p->data[j];
				p->data[j] = p->data[j + 1];
				p->data[j + 1] = tmp;
			}
		}
	}
}

三、头文件、宏定义、函数等声明头文件:game.h

#pragma once

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

// 注:需有意识将变动的常量值用会宏定义
#define MAX 1000
#define NAMEMAX 10
#define SEXMAX 5
#define CALLMAX 10
#define ADDRESSMAX 10


// 在头文件声明一个人物信息结构体类型
typedef struct List
{
	char name[NAMEMAX];
	char sex[SEXMAX];
	int age;
	char call[CALLMAX];
	char address[ADDRESSMAX];
}list;


// 再声明一个真正的通讯录结构体类型
typedef struct Contact
{
	// 可存1000个人的人物信息结构体数组
	list data[MAX];
	// 通讯录中的已存有效信息的数量
	int sz;
}contact;


// 初始化通讯录
void Init(contact* p);

// 判断通讯录是否满
int Is_Full(contact* p);

// 查询通讯录联系人
int Search(contact* p, char name[]);

// 1-添加联系人信息
void Add(contact* p);

// 2-删除指定联系人信息
void Del(contact* p);

// 3 - 查找指定联系人信息
void Find(contact* p);

// 4 - 修改指定联系人信息
void Amd(contact* p);

// 5-显示所有联系人信息
void Print(const contact* p);

// 6-清空所有联系人
void Emp(contact* p);

// 7-以名字排序所有联系人
void Sort(contact* p);

版本③(附加文件存储效果)

一、main函数所在源文件:test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game0122_3.h"

// 实现一个通讯录:(不考虑相同名字)
// 通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
// 提供方法:
// 1-添加联系人信息
// 2-删除指定联系人信息
// 3-查找指定联系人信息
// 4-修改指定联系人信息
// 5-显示所有联系人信息
// 6-清空所有联系人
// 7-以名字排序所有联系人


// 实现一个枚举,使得switch代码的case分支更有意义,之前学过case后可以写枚举类型
enum Option
{
	EXIT,//0
	ADD,
	DEL,
	FIND,
	AMD,
	PRINT,
	EMP,
	SORT
};


void Meun()
{
	printf("--------------------------------------\n");
	printf("----------0-退出----------------------\n");
	printf("----------1-添加联系人信息------------\n");
	printf("----------2-删除指定联系人信息--------\n");
	printf("----------3-查找指定联系人信息--------\n");
	printf("----------4-修改指定联系人信息--------\n");
	printf("----------5-显示所有联系人信息--------\n");
	printf("----------6-清空所有联系人------------\n");
	printf("----------7-以名字排序所有联系人------\n");
	printf("--------------------------------------\n");
}



int main()
{
	// 创建一个数组,实际大小为1000的数组,如果直接将其作为通讯录,
	// 则通讯录中已有多少数据是未知的,后续增删查改打印都无法实现
	// list data[1000] = { 0 };

	// 创建通讯录
	contact con = { 0 };

	// 初始化通讯录(也为之后的优化做铺垫)
	Init(&con);

	int input = 0;
	do {
		Meun();
		printf("请输入您的选项\n");
		scanf("%d", &input);

		switch (input)
		{
		case EXIT:
			// 先保存在文件里
			Save_Con(&con);
			// 先清空,释放空间再退出
			Emp(&con);
			printf("已退出/n");
			break;
		case ADD:
			// 传入结构体地址
			Add(&con);
			break;
		case DEL:
			Del(&con);
			break;
		case FIND:
			Find(&con);
			break;
		case AMD:
			Amd(&con);
			break;
		case PRINT:
			Print(&con);
			break;
		case EMP:
			Emp(&con);
			break;
		case SORT:
			Sort(&con);
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);

	return 0;
}

二、相关函数定义源文件:game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game0122_3.h"


// 将上次创建的通讯录文件内容加载到新创建的con通讯录中
void Load_Con(contact* p)
{
	// 打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL) {
		printf("Load()::%s\n", strerror(errno));
		return;
	}

	// 读文件(二进制输入)
	// fread()函数:返回值的读取的内容数量,当返回值为0时说明读取完毕,
	// 所以不用担心上次保存的内容有多少个用户信息
	// 创建一个临时用户 
	list tmp = { 0 };
	
	while (fread(&tmp, sizeof(list), 1, pf)) {
		// 判断上次保存的用户数量是否大于capacity,如果大于就扩容
		Is_Full(p);

		// 加载用户信息到新通讯录
		p->data[p->sz] = tmp;
		(p->sz)++;
		// 注意:每轮的容量判断指标也要加1
		(p->capacity)++;
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}


// 初始化通讯录
void Init(contact* p)
{
	assert(p);
	(p->sz) = 0;
	(p->capacity) = 0;

	// 第一次扩容DEFAULTAMP个人物信息结构体数量大小并利用calloc函数直接初始化为0
	list* ptr = (list*)calloc(DEFAULTAMP, sizeof(list));
	if (NULL != ptr) {
		// 确保开辟成功再赋值
		(p->data) = ptr;
	}
	else {
		printf("Init()::%s\n", strerror(errno));
		// 有错误提前结束
		return;
	}

	// 将上次创建的通讯录文件内容加载到新创建的con通讯录中
	Load_Con(p);
}


// 判断通讯录本轮开辟空间是否满(增容函数)
void Is_Full(contact* p)
{
	assert(p);
	if (DEFAULTAMP == (p->capacity)) {
		// 扩容:每次扩容DEFAULTAMP个sizeof(list)大小
		list* ptr = (list*)realloc(p->data, (DEFAULTAMP * 2) * sizeof(list));
		if (NULL != ptr) {
			// 确保扩容成功再赋值
			(p->data) = ptr;
			printf("扩容成功\n");

			// 新扩容一次,扩容标记置0
			(p->capacity) = 0;
		}
		else {
			printf("Is_Full()::%s\n", strerror(errno));
			// 无需return,已到函数末尾
		}
	}
}


// 查询通讯录联系人
int Search(const contact* p, char name[]) //const保护通讯录不被改变
{
	int i = 0;
	for (i = 0; i < (p->sz); i++) {
		if (0 == strcmp(name, (p->data[i]).name)) {
			// 查询成功返回下标
			return i;
		}
	}
	// 循环结束仍未返回说明查询失败,返回-1
	return -1;
}


// 1-添加联系人信息
void Add(contact* p)
{
	assert(p);
	// 判断通讯录已开辟空间容量是否已满,如果满了会自动扩容,扩容成功提示
	Is_Full(p);

	// 添加联系人信息
	printf("请输入您要添加的联系人信息:姓名、性别、年龄、电话、住址\n");

	// 注:小心在输入age为int类型时,须带&,其余类似name本身就为数组名,是地址
	scanf("%s %s %d %s %s", (p->data[(p->sz)]).name, (p->data[(p->sz)]).sex, \
		& (p->data[(p->sz)]).age, (p->data[(p->sz)]).call, (p->data[(p->sz)]).address);

	// 有效联系人数量加一并提示成功,本轮已占容量也加一
	(p->sz)++;
	(p->capacity)++;
	printf("增加联系人成功\n");

}


// 2-删除指定联系人信息
void Del(contact* p)
{
	assert(p);
	// 输入需要删除第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后删除
	/*int num = 0;
	printf("请输入您需要删除的联系人序号(从0开始)\n");
	scanf("%d", &num);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再删除
	char name[NAMEMAX] = { 0 };
	printf("请输入您要删除的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int ret = Search(p, name);
	if (-1 == ret) {
		printf("未查询到此人\n");
	}
	else {
		printf("删除成功\n");

		// 将该位置覆盖来删除
		int i = 0;
		/*for (i = ret; i < ((p->sz) - 1); i++) {
			strcpy((p->data[i]).name, (p->data[i + 1]).name);
			strcpy((p->data[i]).sex, (p->data[i + 1]).sex);
			(p->data[i]).age = (p->data[i + 1]).age;
			strcpy((p->data[i]).call, (p->data[i + 1]).call);
			strcpy((p->data[i]).address, (p->data[i + 1]).address);
		}*/
		// 结构体变量很规律,可以相互赋值:(优化版本)
		for (i = 0; i < (p->sz) - 1; i++) {
			(p->data[i]) = (p->data[i + 1]);
		}

		// 有效联系人数量减一,本轮所剩有效容量加一
		(p->sz)--;
		(p->capacity)++;
	}
}


// 3 - 查找指定联系人信息
void Find(contact* p)
{
	assert(p);
	// 输入需要查找第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后打印
	/*int i = 0;
	printf("请输入您需要查找的联系人序号(从0开始)\n");
	scanf("%d", &i);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再打印
	char name[NAMEMAX] = { 0 };
	printf("请输入您要查找的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int i = Search(p, name);
	if (-1 == i) {
		printf("未查询到此人\n");
	}
	else {
		printf("查询成功\n");
		printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			(p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
	}
}


// 4 - 修改指定联系人信息
void Amd(contact* p)
{
	assert(p);
	// 判断通讯录是否为空,如果为空就无法删除并直接返回
	if (0 == (p->sz)) {
		printf("通讯录为空,无法删除\n");
		return;
	}

	// 输入需要修改第几个(从0开始数)联系人(p->sz),也可以根据需求输入名字,查找联系人,然后修改
	/*int i = 0;
	printf("请输入您需要修改的联系人序号(从0开始)\n");
	scanf("%d", &i);*/

	// 根据输入的名字,调用查找函数,根据函数返回值,如果找到再修改
	char name[NAMEMAX] = { 0 };
	printf("请输入您要修改的用户姓名\n");
	scanf("%s", name);

	// 返回值,如果查询到即为下标,未查询到返回-1
	int i = Search(p, name);
	if (-1 == i) {
		printf("未查询到此人\n");
	}
	else {
		// 输入需要修改后的内容
		printf("请输入你所修改后的信息:姓名、性别、年龄、电话、住址\n");
		scanf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			& (p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
		printf("修改完毕\n");
	}
}


// 5-显示所有联系人信息
void Print(const contact* p) //const保护通讯录不被改变
{
	assert(p);
	// 按需求,也可以分开提示并输入
	printf("%s\t%s\t%s\t%s\t%s\n", "姓名", "性别", "年龄", "电话", "住址");

	int i = 0;
	for (i = 0; i < (p->sz); i++) {
		printf("%s\t%s\t%d\t%s\t%s\n", (p->data[i]).name, (p->data[i]).sex, \
			(p->data[i]).age, (p->data[i]).call, (p->data[i]).address);
	}
}


// 6-清空所有联系人
void Emp(contact* p)
{
	assert(p);
	
	// 有效数据置0
	(p->sz) = 0;
	(p->capacity) = 0;

	// 动态内存释放
	free(p->data);
	(p->data) = NULL;
}


// 7-以名字排序所有联系人
void Sort(contact* p)
{
	assert(p);
	// 冒泡排序
	int i = 0;
	int j = 0;
	for (i = 0; i < (p->sz) - 1; i++) {
		for (j = 0; j < (p->sz) - 1 - i; j++) {
			if (strcmp((p->data[j]).name, (p->data[j + 1]).name) > 0) {
				list tmp = p->data[j];
				p->data[j] = p->data[j + 1];
				p->data[j + 1] = tmp;
			}
		}
	}
}

void Save_Con(contact* p)
{
	// 讲:结构体一般用二进制来保存
	// 打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL) {
		printf("Save_Con()::%s\n", strerror(errno));
		return;
	}

	// 写文件(二进制输出)
	int i = 0;
	// 每次写一个,写sz次
	for (i = 0; i < (p->sz); i++) {
		fwrite(&(p->data[i]), 1, sizeof(list), pf);
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}

三、头文件、宏定义、函数等声明头文件:game.h

#pragma once

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

// 注:需有意识将变动的常量值用会宏定义
//#define MAX 1000
#define NAMEMAX 10
#define SEXMAX 5
#define CALLMAX 10
#define ADDRESSMAX 10
// 默认每次扩容的人物信息结构体数量大小
#define DEFAULTAMP 3

// 在头文件声明一个人物信息结构体类型
typedef struct List
{
	char name[NAMEMAX];
	char sex[SEXMAX];
	int age;
	char call[CALLMAX];
	char address[ADDRESSMAX];
}list;


// 再声明一个真正的通讯录结构体类型
typedef struct Contact
{
	// 可存1000个人的人物信息结构体数组
	// list data[MAX]; 存在浪费优化见下
	
	// 创建指针来负责维护通讯录后续动态开辟的内存空间
	list* data;

	// 通讯录每次开辟新空间后目前所占据的空间数量,达到每次开辟新空间数量DEFAULTAMP就触发扩容机制
	int capacity;

	// 通讯录中的已存有效信息的数量
	int sz;
}contact;

// 将上次创建的通讯录文件内容加载到新创建的con通讯录中
void Load_Con(contact* p);

// 初始化通讯录
void Init(contact* p);

// 判断通讯录本轮开辟空间是否满(增容函数)
void Is_Full(contact* p);

// 查询通讯录联系人
int Search(contact* p, char name[]);

// 1-添加联系人信息
void Add(contact* p);

// 2-删除指定联系人信息
void Del(contact* p);

// 3 - 查找指定联系人信息
void Find(contact* p);

// 4 - 修改指定联系人信息
void Amd(contact* p);

// 5-显示所有联系人信息
void Print(const contact* p);

// 6-清空所有联系人
void Emp(contact* p);

// 7-以名字排序所有联系人
void Sort(contact* p);

// 保存通讯录
void Save_Con(contact *p);

总结

这里对文章进行总结:
以上就是今天总结的内容,本文包括了所有个人写的通讯录C语言代码,分享给大家。
真💙欢迎各位给予我更好的建议,欢迎访问!!!小编创作不易,觉得有用可以一键三连哦,感谢大家。peace
希望大家一起坚持学习,共同进步。梦想一旦被付诸行动,就会变得神圣。

欢迎各位大佬批评建议,分享更好的方法!!!🙊🙊🙊

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值