C语言通讯录——动态版&文件版
文章目录
1.前言:
本篇只讲解通讯录中实现动态内存管理的以及文件版本的部分的功能,其它功能请转至本专栏上一篇C语言通讯录静态版学习,我也更推荐先看那个猜能更好理解。
2.思路解析
书接上回,在上篇我们已经一起学习了通讯录静态版,复习一下,就是
首先创建结构体包含联系人各项信息,再创建结构体包含这个联系人结构体和size整型变量(记录联系人数量)
然后实现通讯录的各种功能,包括增删查改排序显示等(通过结构体传址实现)。
最后再实现一下菜单功能
那么现在我们回到正题,我们要怎么实现“动态”呢?
其实很简单,只需要做两件事。
(1)初始化时用malloc申请start_capacity(规定的一个通讯录初始容量)个结构体
(2)当空间不够用时就realloc再申请空间
接下来看看怎么实现吧
3.代码编写
3.1动态版
3.1.1初始化函数
刚才也说了我们要在初始化函数中做修改,不再规定死通讯录容量。而这首先需要我们对结构体做点小变化

我们可以看到我们加了一个新的整型变量capacity,并且将data的数据类型改成了结构体指针。
首先capacity是干什么的?就是用来记录当前通讯录的容量的。而结构体指针也是为了动态内存管理而设
然后我们看看初始化函数中应该作何变动
void InitContact(struct Contact *ps)
{
/*memset(ps->data, 0, sizeof(ps->data));
ps->size = 0;*/
ps->data = (struct PepInfo *) malloc(START_CAPACITY*sizeof(struct PepInfo));
if (ps->data == NULL)
{
return;
}
ps->size = 0;
ps->Capacity = START_CAPACITY;
}
注释的是原先的。我们可以看到,我们用malloc向内存申请了START_CAPACITY个pepinfo大小的空间。这里这个START_CAPACITY实在头文件中使用宏定义定义的初始容量,我这里给的是3.你们可以根据需要自行设定。这里使用malloc(或者说其它内存函数,都要进行检查是否空间申请成功,否则容易造成内存问题)。
3.1.2checkcapacity函数
这个函数是用来干什么的呢?是用来检查通讯录容量是否满,如果满则再用realloc开辟指定大小空间,否则就不需要开辟了。那么具体怎么实现?首先要进行判断是否满。这里用if判断size(联系人数量)和capacity(通讯录容量)作比较。如果相等就满了需要扩容。
这里我选择让用户自己选择扩容多少空间是让操作更灵活,但是后面文件操作时就不怎么方便了,你也可以直接规定每次满扩多少空间,比如原先的两倍之类的
void CheckCapacity(struct Contact* ps)
{
if (ps->size == ps->Capacity)
{
printf("通讯录已满,请扩容\n");
int n = 0;
printf("请输入要扩容的空间>:");
scanf("%d", &n);
struct PepInfo* ptr = realloc(ps->data, (ps->Capacity + n) * sizeof(struct PepInfo));
if (ptr != NULL)
{
ps->data = ptr;
ps->Capacity += n;
printf("扩容成功\n");
}
else
{
printf("扩容失败\n");
}
}
}
完成这个就大功告成了吗,其实不然。我们还要在原先的ADD函数开头加上这个函数进行判断
void AddContact(struct Contact* ps)
{
CheckCapacity(ps);
printf("请输入名字>:");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>:");
scanf("%d", &ps->data[ps->size].age);
printf("请输入性别>:");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>:");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>:");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
/*if (ps->size == MAX)
{
printf("通讯录已满\n");
}
else
{
printf("请输入名字>:");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>:");
scanf("%d", &ps->data[ps->size].age);
printf("请输入性别>:");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>:");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>:");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}*/
}
3.1.3ContactDestroy函数
我们使用内存函数非常非常重要的一件事就是使用完开辟的空间后要free(释放)掉,不然也可能造成内存问题,具体实现很简单直接看代码吧
void DestroyContact(struct Contact* ps)
{
free(ps->data);
ps->data = NULL;
}
这样就好了。要在主函数中退出程序处调用此函数

3.2文件操作
**我们在动态版的基础上进行修改
要使用文件操作相关函数先包含头文件<errno.h>
3.2.1增加功能保存ContactSave函数
首先以写的形式打开文件contact.dat,自己在指定路径下创建。然后通过循环和fwrite函数对信息进行写入操作。写入过后将文件关闭
void SaveContact(struct Contact* ps)
{
FILE* pfWrite = fopen("contact.dat", "wb");
if (pfWrite == NULL)
{
printf("SaveContact::%s\n", strerror(errno));
return;
}
int i = 0;
for ( i = 0; i < ps->size; i++)
{
fwrite(&(ps->data[i]), sizeof(struct PepInfo), 1, pfWrite);
}
printf("保存成功\n");
fclose(pfWrite);
pfWrite = NULL;
}
就这样就可以实现文件的操作了最后到主函数相应地方进行调用

3.2.2加载函数LoadContact
在写完上述函数并测试时,我们会发现我们只是将信息保存到文件去了,实际操作时再次运行时控制台中还是没有相应信息。这是因为我们没有对信息进行加载
要实现此功能首先要选定加载的时机,没错就是在初始化函数内,因为我们程序运行时都会进行一次初始化

就像这样。
然后我们来实现load函数
用临时变量tmp来读取之前存储到文件里的信息然后再将信息传给data。
void LoadContact(struct Contact* ps)
{
struct PepInfo tmp = { 0 };
FILE* pfRead = fopen("Contact.dat", "rb");
if (pfRead == NULL)
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
while (fread(&tmp, sizeof(struct PepInfo), 1, pfRead))
{
CheckCapacity(ps);
ps->data[ps->size] = tmp;
ps->size++;
}
fclose(pfRead);
pfRead = NULL;
}
就这样我们就可以实现文件保存的函数,在下一次运行时这些信息也可以很好的打印在屏幕上了
4.完整代码
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void Menu()
{
printf("******************************\n");
printf("**********通讯录生成成功******\n");
printf("********1.Add***2.Del*********\n");
printf("*******3.Search***4.Show******\n");
printf("*******5.Modify***6.Sort*****\n");
printf("*******7.Save****0.Exit*******\n");
printf("******************************\n");
}
int main()
{
int input = 0;
int n = 0;
struct Contact con;
InitContact(&con);
do
{
Menu();
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case SAVE:
SaveContact(&con);
break;
case EXIT:
printf("即将关闭通讯录,是否保存本次录入信息:\n1.是\n");
scanf("%d", &n);
if (n == 1)
{
SaveContact(&con);
}
DestroyContact(&con);
printf("释放成功,已退出程序\n");
break;
default:
printf("选择错误");
break;
}
} while (input);
return 0;
}
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void InitContact(struct Contact *ps)
{
/*memset(ps->data, 0, sizeof(ps->data));
ps->size = 0;*/
ps->data = (struct PepInfo *) malloc(START_CAPACITY*sizeof(struct PepInfo));
if (ps->data == NULL)
{
return;
}
ps->size = 0;
ps->Capacity = START_CAPACITY;
LoadContact(ps);
}
void CheckCapacity(struct Contact* ps);
void LoadContact(struct Contact* ps)
{
struct PepInfo tmp = { 0 };
FILE* pfRead = fopen("Contact.dat", "rb");
if (pfRead == NULL)
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
while (fread(&tmp, sizeof(struct PepInfo), 1, pfRead))
{
CheckCapacity(ps);
ps->data[ps->size] = tmp;
ps->size++;
}
fclose(pfRead);
pfRead = NULL;
}
void CheckCapacity(struct Contact* ps)
{
if (ps->size == ps->Capacity)
{
struct PepInfo* ptr = realloc(ps->data, (ps->Capacity * 2) * sizeof(struct PepInfo));
if (ptr != NULL)
{
ps->data = ptr;
ps->Capacity *=2 ;
printf("扩容成功\n");
}
else
{
printf("扩容失败\n");
}
}
}
void AddContact(struct Contact* ps)
{
CheckCapacity(ps);
printf("请输入名字>:");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>:");
scanf("%d", &ps->data[ps->size].age);
printf("请输入性别>:");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>:");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>:");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
void ShowContact(const struct Contact* ps)
{
if (ps->size == 0)
{
printf("通讯录暂为空\n");
}
else
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\t\n","名字","年龄","性别","电话","地址");
for (int i = 0; i < ps->size; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\t\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr
);
}
}
static int FindContactbyname(const struct Contact *ps,char name[MAX_NAME])
{
for (int i = 0; i < ps->size; i++)
{
if (0 == strcmp(name, ps->data[i].name))
{
return i;
}
}
return -1;
}
void DelContact(struct Contact* ps)
{
char name[MAX_NAME];
int i = 0;
int pos = 0;
printf("请输入要删除人的名字>:");
scanf("%s", name);
pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("删除的对象不存在\n");
}
else
{
for (int j = pos; j < ps->size; j++)
{
ps->data[j] = ps->data[j + 1];
}
}
printf("删除成功\n");
ps->size--;
}
void SearchContact(const struct Contact* ps)
{
char name[MAX_NAME];
int pos = 0;
printf("请输入要搜索人的名字\n");
scanf("%s", name);
pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("查找的人不存在\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\t\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\t\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr
);
}
}
void ModifyContact(struct Contact* ps)
{
char name[MAX_NAME];
int pos = 0;
printf("请输入要修改人的名称\n");
scanf("%s", name);
pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("要修改的人不存在\n");
}
else
{
printf("请输入名字>:\n");
scanf("%s", ps->data[pos].name);
printf("请输入年龄>:\n");
scanf("%d", &ps->data[pos].age);
printf("请输入性别>:\n");
scanf("%s", ps->data[pos].sex);
printf("请输入电话>:\n");
scanf("%s", ps->data[pos].tele);
printf("请输入地址>:\n");
scanf("%s", ps->data[pos].addr);
printf("修改成功\n");
}
}
const int* SortByName(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return strcmp(pc1->name, pc2->name);
}
const int* SortByAge(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return pc1->age - pc2->age;
}
const int* SortByTele(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return strcmp(pc1->tele, pc2->tele);
}
int (*CompareFunctions[])(const void*, const void*) =
{
SortByName,
SortByAge,
SortByTele
};
void SortContact(struct Contact* ps)
{
int choice;
printf("请选择排序规则\n");
printf("0.按名字排序\n1.按年龄排序\n2.按电话排序\n");
scanf("%d", &choice);
if (choice < 0 || choice >= 3)
{
printf("无效选择\n");
return;
}
qsort(ps->data,
ps->size,
sizeof(struct PepInfo),
CompareFunctions[choice]);
}
void DestroyContact(struct Contact* ps)
{
free(ps->data);
ps->data = NULL;
}
void SaveContact(struct Contact* ps)
{
FILE* pfWrite = fopen("contact.dat", "wb");
if (pfWrite == NULL)
{
printf("SaveContact::%s\n", strerror(errno));
return;
}
int i = 0;
for ( i = 0; i < ps->size; i++)
{
fwrite(&(ps->data[i]), sizeof(struct PepInfo), 1, pfWrite);
}
printf("保存成功\n");
fclose(pfWrite);
pfWrite = NULL;
}
contact.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
//#define MAX 1000
#define START_CAPACITY 3//选择通讯录初始容量
//选择通讯录联系人各项信息的最大字符量
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 20
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
SHOW,
MODIFY,
SORT,
SAVE
};
struct PepInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};
struct Contact
{
struct PepInfo *data ;
int size;
int Capacity;
};
void InitContact(struct Contact* ps);
void AddContact(struct Contact* ps);
void ShowContact(const struct Contact* ps);
void DelContact(struct Contact *ps);
void SearchContact(const struct Contact *ps);
void ModifyContact(struct Contact *ps);
void SortContact(struct Contact *ps);
void DestroyContact(struct Contact *ps);
void SaveContact(struct Contact *ps);
void LoadContact(struct Contact* ps);
5.结语
很快啊动态版和文件操作版就出来了,希望大家可以学到一些东西。也希望可以有人来与我交流不足。
后面,我计划为大家来讲一下数据结构中顺序表和链表(C语言),其实也用了点C++(也只是cout和结构体)。我也是正在学C++和数据结构了。哈哈哈大家一起加油努力学习。

2592

被折叠的 条评论
为什么被折叠?



