通讯录完善版本(详细讲解+源码)

目录

前言

一、使通讯可以动态更新内存

1、contact.h

2、contact.c

存信息:

删除联系人,并试一个不存在的人的信息,看看会不会把其他人删了

​编辑

修改:

​编辑

排序:

​编辑

销毁:

​编辑

​编辑

二、通讯录的信息保存,加载

通讯录的信息保存;

再增加一个保存信息的函数

Save_Contact:

再增加一个从文件中加载信息的函数

Load_Contact:

​编辑

​编辑

三、下面是完整的代码:

test.c

castact.c

contact.h

总结


前言

经过长达六个月的时间,年迈的博主终于想起来,通讯录还需要完善,那么这篇文章就对我们的初代通讯录( 初代通讯录(详细讲解+代码)_化学系通讯录最简单三个步骤-CSDN博客)进行完善,增加动态更新内存的内容,以及建立一个文件,将通讯录的信息保存下来;


一、使通讯可以动态更新内存

我们先看一下原通讯录

1、contact.h
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<math.h>
#pragma once
 
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define MAX 100
 
//定义人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;
 
//定义通讯录的信息
typedef struct Contact
{
	PeoInfo data[MAX];//存放人的信息
	int sz;//记录目前通讯录中存放的人的信息个数
}Contact;
 
//初始化通讯录
void Init_Contact(Contact* pc);
//增加联系人
void Add_Contact(Contact* pc);
//删除联系人
void Del_Contact(Contact* pc);
查找联系人
void Search_Contact(Contact* pc);
修改联系人信息
void Modify_Contact(Contact* pc);
通讯录信息查看
void Show_Contact(Contact* pc);
通讯录信息排列
void Sort_Contact(Contact* pc);
2、contact.c
cpp 复制代码
#include"contact.h"
 
 
//初始化通讯录
void Init_Contact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
 
}
//增加联系人
void Add_Contact(Contact* pc)
{
	assert(pc);
	if (pc->sz == MAX)
	{
		printf("通讯录满了,无法添加");
		return;
    }
	//添加一个联系人的信息
	//printf("请输入你要添加的联系人的信息:");
	//scanf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n", pc->data[pc->sz].name, pc->data[pc->sz].sex, &(pc->data[pc->sz].age), pc->data[pc->sz].tele, pc->data[pc->sz].addr);
	printf("请输入姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &(pc->data[pc->sz].age)); 
	printf("请输入电话:");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
 
}
//删除联系人
void Del_Contact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除");
		return;
 
	}
	//不为空,删除
	printf("请输入要删除人的名字:");
	scanf("%s", name);
	int i = 0;
	int del = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			del = i;
			break;
		}
	}
	for (i = del; i < pc->sz-1; i++)
	{
		pc->data[i] = pc->data[i + 1];
 
	}
	pc->sz--;
	printf("已成功删除该联系人");
 
}
//查找联系人
void Search_Contact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空");
		return;
	}
	printf("请输入要查找的人的名字:");
	scanf("%s", name);
	int i = 0;
	int pos = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			pos = i;			
		}
	}
	printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
	printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",
			pc->data[pos].name,
			pc->data[pos].sex,
			pc->data[pos].age,
			pc->data[pos].tele,
			pc->data[pos].addr);
	
 
 
 
}
//修改联系人信息
void Modify_Contact(Contact* pc) 
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改");
		return;
	}
	printf("请输入要修改的人的名字:");
	scanf("%s", name);
	int i = 0;
	int pos = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			pos = i;
		}
	}
	printf("请输入姓名:");
	scanf("%s", pc->data[pos].name);
	printf("请输入性别:");
	scanf("%s", pc->data[pos].sex);
	printf("请输入年龄:");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入电话:");
	scanf("%s", pc->data[pos].tele);
	printf("请输入地址:");
	scanf("%s", pc->data[pos].addr);
 
}
//通讯录信息查看/打印通讯录
void Show_Contact(Contact* pc)
{
	assert(pc);
	printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr);
	}
 
}
int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
 
//通讯录信息排列
void Sort_Contact(Contact* pc)
{
	
		assert(pc);
		if (pc->sz == 0)
		{
			printf("通讯录为空,无法排序\n");
			return;
		}
		qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);
		printf("排序完成\n");
	
	
 
 
}

可以看到原通讯录是用100大小的静态数组存储信息的,现在我们将其改为动态的数组;

主要是初始化,增加联系人,信息排列,销毁通讯录需要用到动态内存管理;

动态内存函数可以在这里看:动态内存函数_内存创建用什么函数-CSDN博客

我们先将定义进行更改,增加容量capacity,以及将存放信息的data数组不加限制;

然后在初始化Init_Contact这里进行修改,先设置初始容量capacity的值,然后再通过malloc设置初始内存,再用assert断言确保内存设置好了,不为空;

然后就是增加联系人这块,当通讯录满了即pc->sz == pc->capacity的时候,将pc->capacity乘2,增加容量,当然了,想扩大多少倍看你自己喜欢,然后再对存放数据的data数组经行扩容,扩成修改后的capacity大小;最后加个assert断言

然后是信息排列

最后是对动态数组的销毁,先将内存释放掉,然后置为空,再将capacity和sz置为0;

将各个功能都试一下;

存信息:

删除联系人,并试一个不存在的人的信息,看看会不会把其他人删了

修改:

排序:

销毁:

ok,这边的通讯录动态数组已经没问题,我们接着来看

二、通讯录的信息保存,加载

通讯录的信息保存,加载涉及文件的读写操作:c语言文件操作-CSDN博客

通讯录的信息保存;

首先创建一个记事本文档,再在代码中定义上

然后完善增加两个函数的目录

cpp 复制代码
#include"contact.h"
void menu()
{
	printf("**************************************\n");
	printf("****** 1.add     2.del     ***********\n");
	printf("****** 3.search  4.modify  ***********\n");
	printf("****** 5.show    6.sort    ***********\n");
	printf("****** 7.save    8.load    ***********\n");
	printf("****** 0.exit   9.destory  ***********\n");
	printf("**************************************\n");
	
}
enum
{
	Exit,
	Add,
	Del,
	Search,
	Modify,
	Show,
	Sort,
	Save,
	Load,
	Destory
};

//directory
int main()
{//创建通讯录
	Contact con;
	Init_Contact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf("%d", &input);

		switch (input)
		{
		case Exit:printf("已退出通讯录\n");
			break; 
		case Add:Add_Contact(&con);
				break;
		case Del:Del_Contact(&con);
			break;
		case Search:Search_Contact(&con);
			break;
		case Modify:Modify_Contact(&con);
			break;
		case Show:Show_Contact(&con);
			break;
		case Sort:Sort_Contact(&con);
			break;
		case Save:Save_Contact(&con);
			break; 
		case Load:Load_Contact(&con);
		   break;
		case Destory:Destory_Contact(&con); break;
		default:printf("选择错误,请重新选择\n");
			break;
		}	
	} while (input);
	//Destory_Contact(&con);
	return 0;
}

再增加一个保存信息的函数

Save_Contact:

cpp 复制代码
void Save_Contact(Contact* pc) {
	assert(pc);
	FILE* file = fopen(FILENAME, "w");
	if (!file) {
		printf("无法打开文件进行保存\n");
		return;
	}
	for (int i = 0; i < pc->sz; i++) {
		fprintf(file, "%s %s %d %s %s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	fclose(file);
	printf("联系人信息已经保存到文件\n");
}

再增加一个从文件中加载信息的函数

**1. **打开文件**:

  • 使用 `fopen` 函数以只读模式打开指定的文件。如果文件打开失败,程序会输出错误信息并返回。**

**2. **动态扩展内存**:

  • 在读取每个联系人之前,检查当前的联系人数量 `sz` 是否达到了当前容量 `capacity`。如果达到了,就使用 `realloc` 扩展内存,以便可以存储更多的联系人信息。**

**3. **读取数据**:

  • 使用 `fscanf` 从文件中读取每个联系人的信息。格式字符串 `"%s %s %d %s %s\n"` 指定了要读取的数据类型:
  • `%s` 用于读取字符串(姓名、性别、电话、地址)。
  • `%d` 用于读取整数(年龄)。**

**4. **存储数据**:

  • 将读取到的数据存储到 `PeoInfo` 结构体的相应字段中。**

为什么 `age` 需要加引用

在 C 语言中,`fscanf` 函数的工作原理是通过指针来修改变量的值。具体来说:

引用的概念:
在 C 语言中,变量的值是存储在内存中的某个地址。当你想要修改一个变量的值时,你需要提供这个变量的地址。
使用 `&` 符号可以获取变量的地址。例如,`&pc->data[pc->sz].age` 获取 `age` 字段的地址。

fscanf 的参数:
fscanf的参数需要是指向变量的指针,以便它可以直接在内存中修改该变量的值。
对于 age字段,fscanf需要知道它的地址,以便将读取到的整数值存储到这个地址中。因此,必须使用 `&` 符号。

示例:

假设我们有以下代码:

cpp 复制代码
int age;
fscanf(file, "%d", &age);

**`&age` 将 `age` 变量的地址传递给 `fscanf`,这样 `fscanf` 就可以在该地址上写入读取到的整数值。

  • 如果不使用 `&`,例如 `fscanf(file, "%d", age);`,编译器会报错,因为 `age` 是一个整数,而 `fscanf` 需要的是一个指向整数的指针。**

Load_Contact:

cpp 复制代码
void Load_Contact(Contact* pc) {
	assert(pc);
	FILE* file = fopen(FILENAME, "r");
	if (!file) {
		printf("无法打开文件进行加载\n");
		return;
	}
	while (!feof(file)) {
		if (pc->sz == pc->capacity) {
			pc->capacity *= 2;
			pc->data = (PeoInfo*)realloc(pc->data, pc->capacity * sizeof(PeoInfo));//重新分配
			assert(pc->data);//确保内存分配成功
		}
		fscanf(file, "%s %s %d %s %s\n",
			pc->data[pc->sz].name,
			pc->data[pc->sz].sex,
			&pc->data[pc->sz].age,
			pc->data[pc->sz].tele,
			pc->data[pc->sz].addr);
		pc->sz++;
	}
	
	fclose(file);
	printf("联系人信息已经加载\n");
}

再来验一下货,看看我们的代码灵不灵

应该是没有问题

三、下面是完整的代码:
test.c
cpp 复制代码
#include"contact.h"
void menu()
{
	printf("**************************************\n");
	printf("****** 1.add     2.del     ***********\n");
	printf("****** 3.search  4.modify  ***********\n");
	printf("****** 5.show    6.sort    ***********\n");
	printf("****** 7.save    8.load    ***********\n");
	printf("****** 0.exit   9.destory  ***********\n");
	printf("**************************************\n");
	
}
enum
{
	Exit,
	Add,
	Del,
	Search,
	Modify,
	Show,
	Sort,
	Save,
	Load,
	Destory
};

//directory
int main()
{//创建通讯录
	Contact con;
	Init_Contact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf("%d", &input);

		switch (input)
		{
		case Exit:printf("已退出通讯录\n");
			break; 
		case Add:Add_Contact(&con);
				break;
		case Del:Del_Contact(&con);
			break;
		case Search:Search_Contact(&con);
			break;
		case Modify:Modify_Contact(&con);
			break;
		case Show:Show_Contact(&con);
			break;
		case Sort:Sort_Contact(&con);
			break;
		case Save:Save_Contact(&con);
			break; 
		case Load:Load_Contact(&con);
		   break;
		case Destory:Destory_Contact(&con); break;
		default:printf("选择错误,请重新选择\n");
			break;
		}	
	} while (input);
	//Destory_Contact(&con);
	return 0;
}
castact.c
cpp 复制代码
#include"contact.h"


//初始化通讯录
void Init_Contact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	pc->capacity = 2;//设置初始容量
	pc->data = (PeoInfo*)malloc(pc->capacity * sizeof(PeoInfo));//初始内存
	assert(pc->data);//确保内存不为空

}
//增加联系人
void Add_Contact(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//printf("通讯录满了,无法添加");
         //return;
		 pc->capacity *= 2;
		 pc->data = (PeoInfo*)realloc(pc->data,pc->capacity*sizeof(PeoInfo));
		 assert(pc->data);//确保内存分配成功

    }
	//添加一个联系人的信息
	//printf("请输入你要添加的联系人的信息:");
	//scanf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n", pc->data[pc->sz].name, pc->data[pc->sz].sex, &(pc->data[pc->sz].age), pc->data[pc->sz].tele, pc->data[pc->sz].addr);
	printf("请输入姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &(pc->data[pc->sz].age)); 
	printf("请输入电话:");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入地址:");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;

}
//删除联系人
void Del_Contact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除");
		return;

	}
	//不为空,删除
	printf("请输入要删除人的名字:");
	scanf("%s", name);
	int i = 0;
	int del = -1;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			del = i;
			break;
		}
	}
	if (del == -1) {
		printf("未找到该联系人");
		return;
	}
	for (i = del; i < pc->sz-1; i++)
	{
		pc->data[i] = pc->data[i + 1];

	}
	pc->sz--;
	printf("已成功删除该联系人");

}
//查找联系人
void Search_Contact(Contact* pc)
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空");
		return;
	}
	printf("请输入要查找的人的名字:");
	scanf("%s", name);
	int i = 0;
	int pos = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			pos = i;	
			printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
			printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",
				pc->data[pos].name,
				pc->data[pos].sex,
				pc->data[pos].age,
				pc->data[pos].tele,
				pc->data[pos].addr);
			return;
		}
	}
	
	printf("未找到该联系人");



}
//修改联系人信息
void Modify_Contact(Contact* pc) 
{
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改");
		return;
	}
	printf("请输入要修改的人的名字:");
	scanf("%s", name);
	int i = 0;
	int pos = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			pos = i;
			printf("请输入姓名:");
			scanf("%s", pc->data[pos].name);
			printf("请输入性别:");
			scanf("%s", pc->data[pos].sex);
			printf("请输入年龄:");
			scanf("%d", &(pc->data[pos].age));
			printf("请输入电话:");
			scanf("%s", pc->data[pos].tele);
			printf("请输入地址:");
			scanf("%s", pc->data[pos].addr);
			return;
		}
	}
	printf("未找到该联系人");


}
//通讯录信息查看/打印通讯录
void Show_Contact(Contact* pc)
{
	assert(pc);
	printf("%-20s\t%-5s\t%-4s\t%-12s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5s\t%-4d\t%-12s\t%-20s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr);
	}

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

//通讯录信息排列
void Sort_Contact(Contact* pc)
{
	
		assert(pc);
		if (pc->sz == 0)
		{
			printf("通讯录为空,无法排序\n");
			return;
		}
		//qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);
		qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
		printf("排序完成\n");
	
	


}
void Destory_Contact(Contact* pc)
{
	assert(pc);
	free(pc->data);//释放内存
	/*if (pc->sz == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	for (int i =pc->sz; i >=0; i--)
	{
		pc->sz--;
		pc->data[i] = pc->data[i + 1];
	}*/
	pc->data = NULL;//置空
	pc->sz = 0;
	pc->capacity = 0;
	printf("已全部删除,通讯录销毁\n");


}


void Save_Contact(Contact* pc) {
	assert(pc);
	FILE* file = fopen(FILENAME, "w");
	if (!file) {
		printf("无法打开文件进行保存\n");
		return;
	}
	for (int i = 0; i < pc->sz; i++) {
		fprintf(file, "%s %s %d %s %s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr);
	}
	fclose(file);
	printf("联系人信息已经保存到文件\n");
}


void Load_Contact(Contact* pc) {
	assert(pc);
	FILE* file = fopen(FILENAME, "r");
	if (!file) {
		printf("无法打开文件进行加载\n");
		return;
	}
	while (!feof(file)) {
		if (pc->sz == pc->capacity) {
			pc->capacity *= 2;
			pc->data = (PeoInfo*)realloc(pc->data, pc->capacity * sizeof(PeoInfo));//重新分配
			assert(pc->data);//确保内存分配成功
		}
		fscanf(file, "%s %s %d %s %s\n",
			pc->data[pc->sz].name,
			pc->data[pc->sz].sex,
			&pc->data[pc->sz].age,
			pc->data[pc->sz].tele,
			pc->data[pc->sz].addr);
		pc->sz++;
	}
	
	fclose(file);
	printf("联系人信息已经加载\n");
}
contact.h
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<math.h>
#pragma once
#define FILENAME "contacts.txt"//文件名

#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
//#define MAX 1000

//定义人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tele[TELE_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

//定义通讯录的信息
typedef struct Contact
{
	//PeoInfo data[MAX];//存放人的信息
	PeoInfo* data;//存放人的信息
	int sz;//记录目前通讯录中存放的人的信息个数
	int capacity;//记录目前容量
}Contact;

//初始化通讯录
void Init_Contact(Contact* pc);
//增加联系人
void Add_Contact(Contact* pc);
//删除联系人
void Del_Contact(Contact* pc);
查找联系人
void Search_Contact(Contact* pc);
修改联系人信息
void Modify_Contact(Contact* pc);
通讯录信息查看
void Show_Contact(Contact* pc);
通讯录信息排列
void Sort_Contact(Contact* pc);
//通讯录删除全部联系人
void Destory_Contact(Contact* pc);

void Save_Contact(Contact* pc); //保存

void Load_Contact(Contact* pc);//加载

总结

首先非常感谢大家的观看,这期对通讯录进行了完善,增加了动态变化数组已经信息的保存与加载

修改了很多缺陷:

1.这个通讯录能保存了,不会出现一旦退出就消失了

2.这个通讯录没有固定大小,减少了空间的浪费

本期内容到这里就结束了,有什么不足的地方可以提出来,我会虚心接受改进的,大家一起加油吧

相关推荐
金融小师妹13 分钟前
应用BERT-GCN跨模态情绪分析:贸易缓和与金价波动的AI归因
大数据·人工智能·算法
广州智造20 分钟前
OptiStruct实例:3D实体转子分析
数据库·人工智能·算法·机器学习·数学建模·3d·性能优化
belldeep2 小时前
如何阅读、学习 Tcc (Tiny C Compiler) 源代码?如何解析 Tcc 源代码?
c语言·开发语言
LuckyTHP2 小时前
java 使用zxing生成条形码(可自定义文字位置、边框样式)
java·开发语言·python
Trent19852 小时前
影楼精修-肤色统一算法解析
图像处理·人工智能·算法·计算机视觉
feifeigo1232 小时前
高光谱遥感图像处理之数据分类的fcm算法
图像处理·算法·分类
北上ing3 小时前
算法练习:19.JZ29 顺时针打印矩阵
算法·leetcode·矩阵
.格子衫.4 小时前
真题卷001——算法备赛
算法
XiaoyaoCarter5 小时前
每日一道leetcode
c++·算法·leetcode·职场和发展·二分查找·深度优先·前缀树
Blossom.1185 小时前
使用Python实现简单的人工智能聊天机器人
开发语言·人工智能·python·低代码·数据挖掘·机器人·云计算