目录
🤑通讯录功能介绍
该篇幅时使用动态内存分配的知识---------------------🤑尽情期待哦!
修改1:初始化函数
修改2:新增了一个扩容的函数
修改3:我们还增加一个动态内存释放的函数。
修改4:新增文件存储数据。
🥵通讯录相关功能介绍
我们先说一下我们想要实现的几个功能!
功能有以下几点:
先看一下实现后的效果:
🥵菜单打印
🥵添加个人信息
🥵显示个人信息内容
🥵查找个人信息内容
🥵修改个人信息内容
🥵排序个人信息内容
🥵删除个人信息内容
🤑分析每一个函数的功能实现
🥵数据结构创建的数据类型
定义结构类型:
第一层是人的基本信息,第二层是将全局的指针和人的基本信息结构封装到一起,方便使用。
**这里我们将结构体进行了修改
🥵首次的数据区初始化函数
在代码首次开启的时候,我们先将存储数据的数据区进行动态内存分配。
🥵主菜单/与Main函数调用架构
🥵主菜单函数的实现
🥵打印进度条函数实现
此代码在我的另一篇博客里已经明确讲解,这里我就不进行解析了。
🥵通讯录数组区增加函数实现
1:对数据使用assert是为了保护指针不是NULL指针,已保证程序运行的安全性。
2:在录入信息前,我们先进行判断全局的指针是否为100,如果是100说明数据存储区已经满了,这样就不能进行数据存储啦,所以直接返回。
3:存储数据没有满,我们分别对姓名、年龄、性别、电话、家庭地址进行数据获取,并且获取完成后进行数据条打印,以达到美观效果。
**这个增加函数我们进行了修改,并多写了一个扩容的函数
🥵通讯录数组区动态内存释放函数实现
当动态内存释放的时候,我们需要调用这个函数,将内存空间释放掉,并且将指针改为空指针。
🥵通讯录数组区显示函数实现
这个其实没有什么好说的,将进度条打印的时机放到合适的位置,先打印出格式姓名、年龄、性别、电话、家庭地址的目录,printf上的打印格式是标准的格式,不明白的小伙伴可以区百度查一下哦---🥵
然后格式确定后,我们就直接遍历结构体中的数组进行分行显示就好啦--🥵
🥵通讯录数组区删除函数实现
删除的函数我们同样对指针进行断言一下
1:同样的我们首先判断一下存储器中是否有数据,要是没有数据提示一下return
2:如果存储区中有数据,我们还需要判断一下想要删除的那个个人信息的姓名在不在存储中存储,我们封装了一个函数,以方便其他的函数也可以共享这个函数进行使用,如果没有也提示一下,并且return
3:上面两个条件都成立的情况下我们就可以进行删除数据啦,删除的逻辑其实也很简单,其实就是找到那组数据到存储器中总人头数,低位替换成高位就可以啦,我相信有一点点C语言的小伙伴肯定明白。
🥵通讯录数组区查找函数实现
查找数据并且显示的函数其实和显示函数一样
1:前面的逻辑一样,调用查找姓名的函数来进行查找是否有相同名字的人。
2:如果找到了,直接按照显示的代码进行打印,就是打印一个人的信息,所以FOR循环就可以去掉啦。
🥵通讯录数组区修改函数实现
修改函数的代码也是和前面一样的逻辑,当找到了要修改人的指针下表,我们可以让用户在重新输入要修改的内容。
🥵通讯录数组区排序函数实现
排序函数我们可以调用现成的qsort()库函数来进行实现,当然需要练习的小伙伴也可以按照我上一篇博客自己写一个qsort()模拟实现。
以下本人为了自己练习所有两种方式都进行了实现。
这里我也推荐小伙伴们自己写一下,作为练习嘛 -------🥰之前的博客有关于MyQsort的模拟实现解析。
🥵通讯录的文件存储
🤑代码整体展示
🥵主程序代码
#include"Communicate.h"
int main()
{
// 封装的二层结构体进行实例化。
CommunicateData pCommData;
// 我对结构体数据区进行数据初始化,并且扩容出一定的空间。
if (CommunicateDataInfo(&pCommData) == 1)
{
printf("----------> 通讯录初始化内存空间失败!\n");
}
// 程序走在这里就可以正常的进行游戏了。
int input = 0;
do
{
// 调用打印菜单函数。
Menu();
scanf("%d", &input);
switch (input)
{
case 1:
system("cls");
CommunicateAdd(&pCommData); // 调用通讯录增加函数
break;
case 2:
system("cls");
// 调用通讯录删除函数。
CommunicateDelete(&pCommData);
break;
case 3:
system("cls");
// 调用通讯录查找函数
CommunicateSearch(&pCommData);
break;
case 4:
system("cls");
// 调用通讯修改找函数
CommunicateModfiy(&pCommData);
break;
case 5:
system("cls");
CommunicateShow(&pCommData);
break;
case 6:
system("cls");
// 调用通讯排序找函数
CommunicateSort(&pCommData);
break;
case 0:
// 调用保存写入文件函数
CommunicateSave(&pCommData);
// 调用销毁动态开辟空间和指针的函数。
CommunicateDestory(&pCommData);
printf("小程序已退出-->\n");
break;
default:
system("cls");
printf("小程序暂时未开放次功能-->\n");
break;
}
system("pause");
system("cls");
} while (input);
return 0;
}
🥵Communicate.c功能代码
#include"Communicate.h"
// 打印菜单函数 - 函数实现。
void Menu()
{
printf("########################################################\n");
printf("##----------------------功能目录----------------------##\n");
printf("##----------------------------------------------------##\n");
printf("## 1- Add 2- Delete ##\n");
printf("##----------------------------------------------------##\n");
printf("## 3- Search 4- Modfiy ##\n");
printf("##----------------------------------------------------##\n");
printf("## 5- Show 6- Sort ##\n");
printf("##----------------------------------------------------##\n");
printf("## 0- Exit ##\n");
printf("########################################################\n");
printf("请以数字的形式选择功能-->\n");
}
// 打印进度条函数 - 实现
void ProgressBar()
{
char bar[BAR_NUM];
char symbol[] = { '|','/','-','\\' };
memset(bar, '\0', sizeof(bar));
// 初始化完成后,开始进行打印。
int i = 0;
for (i = 0; i < BAR_NUM; i++)
{
printf("[%-100s][%-3d%%][%c]\r", bar, i, symbol[i % 4]);
bar[i] = '#';
Sleep(10);
}
printf("\n");
}
// 运行扩容 - 函数实现。
void CommunicateCapacity(CommunicateData* pCommData)
{
// 断言:保护指针形参
assert(pCommData);
// 运行进行扩容
PeopleMassage* temp = (PeopleMassage*)realloc(pCommData->Data, (pCommData->capacity + CAPACITY_INFOSIZE) * sizeof(PeopleMassage));
if (temp == NULL) // 确保扩容成功
{
perror("realloc");
return;
}
// 初始化扩容,程序走到这里说明扩容成功!
pCommData->Data = temp;
temp = NULL;
pCommData->capacity += CAPACITY_INFOSIZE;
printf("已扩容成功!\n");
}
// 数据区初始化扩容。 - 函数实现。
int CommunicateDataInfo(CommunicateData* pCommData)
{
// 断言:保护指针形参
assert(pCommData);
// 开辟内存
pCommData->Data = (PeopleMassage*)calloc(CAPACITY_INFOSIZE, sizeof(PeopleMassage));
if (pCommData->Data == NULL) // 确保扩容成功
{
perror("calloc");
return 1;
}
// 初始化扩容,程序走到这里说明扩容成功!
pCommData->pointer = 0;
pCommData->capacity = CAPACITY_INFOSIZE;
// 读取文件中的数据
CommunicateUpload(pCommData);
return 0;
}
// 销毁动态开辟空间和指针 - 函数实现。
void CommunicateDestory(CommunicateData* pCommData)
{
// 断言:保护指针形参
assert(pCommData);
free(pCommData->Data); // 消除指针指向的数据。
pCommData->Data = NULL; // 消除此指针变量。
}
// 通讯录增加 - 函数实现。
void CommunicateAdd(CommunicateData* pCommData)
{
// 断言:保护指针形参
assert(pCommData);
// 判断通讯录的容量是否满了,满了先进行扩容。
if (pCommData->pointer == pCommData->capacity)
{
// 进行扩容。
CommunicateCapacity(pCommData);
}
else// 程序走到这里说明通讯录的个数没有满,可以继续存放个人信息。
{
printf("请输入存储人 - 姓名:> ");
scanf("%s", &pCommData->Data[pCommData->pointer].name);
printf("请输入存储人 - 年龄:> ");
scanf("%d", &pCommData->Data[pCommData->pointer].age);
printf("请输入存储人 - 性别:> ");
scanf("%s", &pCommData->Data[pCommData->pointer].gender);
printf("请输入存储人 - 电话:> ");
scanf("%s", &pCommData->Data[pCommData->pointer].phone);
printf("请输入存储人 - 家庭住址:> ");
scanf("%s", &pCommData->Data[pCommData->pointer].address);
}
// 打印进度条(美观)
ProgressBar();
printf("保存成功-->\n");
pCommData->pointer++;
}
static int FindByName(CommunicateData* pCommData, char* name)
{
// 遍历结构体数据中的name。
for (int i = 0; i < pCommData->pointer; i++)
{
if (strcmp(pCommData->Data[i].name, name) == 0)
return i;
}
return -1;
}
// 通讯录删除 - 函数声明。
void CommunicateDelete(CommunicateData* pCommData)
{
// 断言:保护指针形参
assert(pCommData);
// 第一步:我们要确认一下通讯录中是否有数据。
if (pCommData == 0)
{
printf("通讯录中没有个人信息数据--->\n");
return;
}
// 第二步:程序走在这里说明通讯录有个人数据,我们让用户输入要进行操作人的名字。
char name[NAME_MAX] = { 0 };
printf("请输入要删除个人信息的姓名---> ");
scanf("%s", &name);
// 第三步:先找是否有相同人的名,没有返回-1,有的话返回这个人在数组的下标。
// 查找
int posPointer = FindByName(pCommData, name);
if (posPointer == -1)
{
printf("没有该个人信息数据,请输入正确的姓名--->\n");
return;
}
// 程序走到这里说明找到了相同姓名的人。
for (int i = 0; i < pCommData->pointer - 1; i++)
{
pCommData->Data[i] = pCommData->Data[i + 1];
}
// 读进度条 , 提示删除成功字样 - 将全局指针减1。
ProgressBar();
printf("删除成功-->\n");
pCommData->pointer--;
}
// 通讯录数组区查找函数 - 函数实现。
void CommunicateSearch(CommunicateData* pCommData)
{
assert(pCommData);
// 程序走到这里说明通讯录中有相关的个人信息。
printf("请输入要查找个人信息的姓名---> ");
char name[NAME_MAX] = { 0 };
scanf(" %s", &name);
// 删除分为两个阶段:1、查找要查找人的名字 2、查找个人信息。
int pointPos = FindByName(pCommData, name);
if (pointPos == -1)
{
printf("没有该个人信息数据,请输入正确的姓名--->\n");
return;
}
// 程序走到这里说明通讯录数组中有要查找人的名字。
// 读进度条 , 提示删除成功字样 - 将全局指针减1。
ProgressBar();
printf("查询成功-->\n");
// 直接对指针指向的数据组进行打印。
printf("%-10s\t%-3s\t%-4s\t%-11s\t%-50s\n", "姓名", "年龄", "性别", "电话", "地址");
// 打印通讯录数组区的内容。
printf("%-10s\t%-3d\t%-4s\t%-11s\t%-50s\n", pCommData->Data[pointPos].name,
pCommData->Data[pointPos].age,
pCommData->Data[pointPos].gender,
pCommData->Data[pointPos].phone,
pCommData->Data[pointPos].address);
}
// 通讯录数组区修改函数 - 数声明
void CommunicateModfiy(CommunicateData* pCommData)
{
assert(pCommData);
// 程序走到这里说明通讯录中有相关的个人信息。
printf("请输入要修改个人信息的姓名---> ");
char name[NAME_MAX] = { 0 };
scanf(" %s", &name);
// 删除分为两个阶段:1、查找要查找人的名字 2、查找个人信息。
int pointPos = FindByName(pCommData, name);
if (pointPos == -1)
{
printf("没有该个人信息数据,请输入正确的姓名--->\n");
return;
}
// 程序走到这里说明通讯录数组中有要修改人的名字。
// 删除分为两个阶段:1、查找要查找人的名字 2、查找个人信息。
printf("要修改人的信息已经找到,接下来进行修改数据--->\n");
// 程序走到这里说明通讯录的个数没有满,可以继续存放个人信息。
printf("请输入存储人 - 姓名:> ");
scanf("%s", &pCommData->Data[pointPos].name);
printf("请输入存储人 - 年龄:> ");
scanf("%d", &pCommData->Data[pointPos].age);
printf("请输入存储人 - 性别:> ");
scanf("%s", &pCommData->Data[pointPos].gender);
printf("请输入存储人 - 电话:> ");
scanf("%s", &pCommData->Data[pointPos].phone);
printf("请输入存储人 - 家庭住址:> ");
scanf("%s", &pCommData->Data[pointPos].address);
// 读进度条 , 提示保存成功字样 - 将全局指针加1。
ProgressBar();
printf("修改成功-->\n");
}
// 通讯录打印 - 函数实现。
void CommunicateShow(CommunicateData* pCommData)
{
// 断言:保护指针形参
assert(pCommData);
// 打印进度条(美观)
ProgressBar();
// 打印标题
printf("%-10s\t%-3s\t%-4s\t%-11s\t%-50s\n", "姓名", "年龄", "性别", "电话", "地址");
// 打印通讯录数组区的内容。
int i = 0;
for (i = 0; i < pCommData->pointer; i++)
{
printf("%-10s\t%-3d\t%-4s\t%-11s\t%-50s\n", pCommData->Data[i].name,
pCommData->Data[i].age,
pCommData->Data[i].gender,
pCommData->Data[i].phone,
pCommData->Data[i].address);
}
}
int CompareName(const void* num1, const void* num2)
{
assert(num1 && num2);
return strcmp(((PeopleMassage*)num2)->name, ((PeopleMassage*)num1)->name);
}
// 模拟qsort的Swap函数实现。
void Swap(char* num1, char* num2, int wigth)
{
int i = 0;
for (i = 0; i < wigth; i++)
{
char temp = *num1;
*num1 = *num2;
*num2 = temp;
num1++;
num2++;
}
}
// 模拟qsort函数的实现。
void MyQsort(void* base, int size, int wigth, int CompareName(const void* num1, const void* num2))
{
assert(base);
int i = 0;
for (i = 0; i < size - 1; i++)
{
int j = 0;
for (j = 0; j < size - 1 - i; j++)
{
if (CompareName((char*)base + j * wigth, (char*)base + (j + 1) * wigth) > 0)
{
// 交换数据
Swap((char*)base + j * wigth, (char*)base + (j + 1) * wigth, wigth);
}
}
}
}
// 通讯录数组区排序函数 - 实现
void CommunicateSort(CommunicateData* pCommData)
{
// 调用qsort函数库实现排序。
//qsort(pCommunicate->peoMessData, pCommunicate->pointer, sizeof(PeopleMessage), CompareName);
// 调用自己编写的qsort函数库实现排序。
MyQsort(pCommData->Data, pCommData->pointer, sizeof(PeopleMassage), CompareName);
ProgressBar();
printf("排序成功-->\n");
}
// 通讯录写入文件函数 - 函数实现。
void CommunicateSave(CommunicateData* pCommData)
{
// 打开文件
FILE* pfWrite = fopen("Communicate.txt", "wb");
if (pfWrite == NULL)
{
perror("CommunicateSave");
return;
}
// 写入数据
for (int i = 0; i < pCommData->pointer; i++)
{
fwrite(pCommData->Data + i, sizeof(PeopleMassage), 1, pfWrite);
}
// 关闭文件
fclose(pfWrite);
pfWrite = NULL;
}
// 通讯录上传文件函数 - 函数实现。
void CommunicateUpload(CommunicateData* pCommData)
{
// 打开文件
FILE* pfRead = fopen("Communicate.txt", "rb");
if (pfRead == NULL)
{
perror("CommunicateUpload");
return;
}
// 上传信息(考虑增容问题)
PeopleMassage temp = { 0 };
while (fread(&temp, sizeof(PeopleMassage), 1, pfRead) == 1)
{
if (pCommData->pointer == pCommData->capacity)
{
CommunicateCapacity(pCommData);
}
pCommData->Data[pCommData->pointer] = temp;
pCommData->pointer++;
}
// 关闭文件
fclose(pfRead);
pfRead = NULL;
}
🥵Communicate.h头文件代码
#define _CRT_SECURE_NO_WARNINGS
#define BAR_NUM 101 // 进度条长度
#define NAME_MAX 10 // 通讯录 - 名字 - 字符总大小定义。
#define GENDER_MAX 4 // 通讯录 - 性别 - 字符总大小定义。
#define PHONE_MAX 11 // 通讯录 - 电话 - 字符总大小定义。
#define ADDRESS_MAX 50 // 通讯录 - 地址 - 字符总大小定义。
#define CAPACITY_INFOSIZE 3 // 通讯录 - 初始化扩容的大小
#include<stdio.h>
#include<windows.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
// 创建结构体的第一层:内容是人的基本信息(姓名、性别、电话、地址、年龄)。
typedef struct PeopleMassage
{
char name[NAME_MAX]; // 姓名
char gender[GENDER_MAX]; // 性别
char phone[PHONE_MAX]; // 电话
char address[ADDRESS_MAX]; // 地址
int age; // 年龄
}PeopleMassage;
// 创建结构体的第二层:内容是数据存储容量、全局指针、和容量的记录
typedef struct CommunicateData
{
PeopleMassage* Data; // 指针类型的结构体成员(人的基本信息)
int pointer; // 操控全局的指针
int capacity; // 已经扩容的容量
}CommunicateData;
// 数据区初始化扩容 - 函数声明。
int CommunicateDataInfo(CommunicateData* pCommData);
// 运行扩容 - 函数声明。
void CommunicateCapacity(CommunicateData* pCommData);
// 销毁动态开辟空间和指针 - 函数声明。
void CommunicateDestory(CommunicateData* pCommData);
// 打印菜单函数 - 函数声明。
void Menu();
// 打印进度条函数 - 函数声明。
void ProgressBar();
// 通讯录增加 - 函数声明。
void CommunicateAdd(CommunicateData* pCommData);
// 通讯录数组区查找函数 - 函数声明。
void CommunicateSearch(CommunicateData* pCommunicate);
// 通讯录删除 - 函数声明。
void CommunicateDelete(CommunicateData* pCommData);
// 通讯录查找函数 - 函数声明
void CommunicateModfiy(CommunicateData* pCommunicate);
// 通讯录排序函数 - 函数声明
void CommunicateSort(CommunicateData* pCommunicate);
// 通讯录打印函数 - 函数声明。
void CommunicateShow(CommunicateData* pCommData);
// 通讯录写入文件函数 - 函数声明。
void CommunicateSave(CommunicateData* pCommData);
// 通讯录上传文件函数 - 函数声明。
void CommunicateUpload(CommunicateData* pCommData);