写程序前我对整体框架进行了分析,在写的过程中对功能不断进行扩充,此通讯录共经历了三版。
整体思路
功能分析具体如下:
功能实现:
第一版通讯录:为静态通讯录。实现要求的增、删、改、查、功能。
信息未录入时,已按最大容量1000申请内存,会存在浪费内存或内存不够用的问题。
第二版通讯录:为动态增长通讯录。对空间的使用进行了升级。为方便验证,刚开始默认可以存放3个人信息,当发现当前通讯录满时,我们进行扩容,每次增加2个空间。
第三版通讯录:文件版本通讯录。进行日志记录,关键信息写到文件中。添加功能7将数据保存到文件中,在程序进行内容释放前,再次调用,save函数防止遗忘保存。为了在每次重新跑程序时,将已存文件加载到通讯录中,重新定义通讯录初始化,读取已保存文件。
一、关键解析
1、枚举与switch语句的联合: 使用枚举enum 与switch语句进行连接,增加代码的可读性。
为菜单书写功能函数时,不必在来回看每个数对应哪一个函数。
2、函数的定义与调用: 写代码过程中发现删除、修改、展示都需查找功能,出现代码冗余。
为消除代码冗余,剥离了公共函数,定义成static函数,在本文件内部调用。
3、 自定义数据类型-结构体:使用结构体将多个变量进行打包,方便赋值,调用。
在未使用typedef时每次写结构体变量,前面都要加struct,一不小心漏写,vs就进行报错,很麻烦而且语句较为拖沓。用typedef重命名后,后续代码进行书写时带来了方便。
4、动态内存分配: 使用malloc(sizeof)函数进行动态内存的分配,在增加数据时,进行容量检测,如果容量不够进行扩容,若容量够用,正常进行数据的添加。
最后将动态开辟的内容进行释放。以此完成了静态通讯录向动态扩容通讯录的转变。
5、文件操作:文件的保存与读写,将保存好的信息直接在通讯录初始化时进行加载,实现了记录功能。
具体实现,定义了一个加载函数,在初始化时进行读取文件,若文件存在,判断内存是否够用,不够用进行扩容,够用则进行加载。
二、具体实现
1.定义通讯录类型
代码如下:
typedef struct Person
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}Person;
//定义通讯录类型
typedef struct Contact
{
struct Person* data;//存放用户信息
int size;//记录当前已有元素个数
int capacity;//记录通讯录当前最大容量
}Contact;
2.初始化通讯录
这里初始化过程中,对文件进行了加载,代码如下:
void InitContact(struct Contact* ps)
{
ps->data = (struct Person*)malloc(Begin * sizeof(struct Person));
if (ps->data == NULL)
{
return;
}
ps->size = 0;
ps->capacity = Begin;
//把文件中已存放的通讯录信息,加载到通讯录
LoadContact(ps);
}
void LoadContact(Contact* ps)
{
Person tmp = { 0 };
FILE* pfRead = fopen("contact.txt", "rb");
if (pfRead == NULL)
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
//读取文件,存放到通讯录
while (fread(&tmp, sizeof(Person), 1, pfRead))
{
CheckCapacity(ps);
ps->data[ps->size] = tmp;
ps->size++;
}
fclose(pfRead);
pfRead = NULL;
}
3.检查内存进行增容
代码如下:
CheckCapacity(struct Contact* ps)
{
if (ps->size == ps->capacity)
{
//增容
struct Person* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(struct Person));
if (ptr != NULL)
{
ps->data = ptr;
ps->capacity += 2;
printf("增容成功\n");
}
else
{
printf("增容失败\n");
}
}
}
4.添加信息
代码如下:
void AddContact(struct Contact* ps)
{
//检查当前通讯录容量
//1.如果容量满了,增容
//2.如果未满,无操作;
CheckCapacity(ps);
//增加数据
printf("请输入名字>\n");
//放到下标为size的data数组中
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>\n");
scanf("%d", &(ps->data[ps->size].age));
//年龄为整型,与其他几个数组不同所以应取地址。
printf("请输入性别>\n");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>\n");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>\n");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
5.增、删、查都会用 索引
代码复用如下:
void AddContact(struct Contact* ps)
{
//检查当前通讯录容量
//1.如果容量满了,增容
//2.如果未满,无操作;
CheckCapacity(ps);
//增加数据
printf("请输入名字>\n");
//放到下标为size的data数组中
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>\n");
scanf("%d", &(ps->data[ps->size].age));
//年龄为整型,与其他几个数组不同所以应取地址。
printf("请输入性别>\n");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>\n");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>\n");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
6.保存文件
代码如下:
void SaveContact(Contact* ps)
{
FILE* pfWrite = fopen("contact.txt", "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(Person), 1, pfWrite);
}
fclose(pfWrite);
pfWrite = NULL;
}
# 三、完整代码 ## Contect.h
代码如下
#pragma once
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#define MAX 1000
#define Begin 3//初始容量
//使用宏定义方便以后对代码的更改
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
enum option
{
EXIT,//0
ADD,//1
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
SAVE
};
typedef struct Person
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}Person;
//定义通讯录类型
typedef struct Contact
{
struct Person* data;//存放用户信息
int size;//记录当前已有元素个数
int capacity;//记录通讯录当前最大容量
}Contact;
//进行函数声明
//初始化函数通讯录
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 DestroyContact(Contact* ps);
//保存文件
void SaveContact(Contact* ps);
//加载文件中的信息到通讯录
void LoadContact(Contact* ps);
Contect.c
代码如下
#define _CRT_SECURE_NO_WARNINGS 1
//实现函数功能
#include "Contect.h"
void InitContact(struct Contact* ps)
{
ps->data = (struct Person*)malloc(Begin * sizeof(struct Person));
if (ps->data == NULL)
{
return;
}
ps->size = 0;
ps->capacity = Begin;
//把文件中已存放的通讯录信息,加载到通讯录
LoadContact(ps);
}
CheckCapacity(ps);//函数声明
void LoadContact(Contact* ps)
{
Person tmp = { 0 };
FILE* pfRead = fopen("contact.txt", "rb");
if (pfRead == NULL)
{
printf("LoadContact::%s\n", strerror(errno));
return;
}
//读取文件,存放到通讯录
while (fread(&tmp, sizeof(Person), 1, pfRead))
{
CheckCapacity(ps);
ps->data[ps->size] = tmp;
ps->size++;
}
fclose(pfRead);
pfRead = NULL;
}
CheckCapacity(struct Contact* ps)
{
if (ps->size == ps->capacity)
{
//增容
struct Person* ptr = realloc(ps->data, (ps->capacity + 2) * sizeof(struct Person));
if (ptr != NULL)
{
ps->data = ptr;
ps->capacity += 2;
printf("增容成功\n");
}
else
{
printf("增容失败\n");
}
}
}
void AddContact(struct Contact* ps)
{
//检查当前通讯录容量
//1.如果容量满了,增容
//2.如果未满,无操作;
CheckCapacity(ps);
//增加数据
printf("请输入名字>\n");
//放到下标为size的data数组中
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>\n");
scanf("%d", &(ps->data[ps->size].age));
//年龄为整型,与其他几个数组不同所以应取地址。
printf("请输入性别>\n");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>\n");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>\n");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
void ShowContact(const struct Contact* ps)
{
if (ps->size == 0)
{
printf("通讯录为空\n");
}
else
{
int i = 0;
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < ps->size; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr);
}
}
}
static int FindByName(const struct Contact* ps, char name[MAX_NAME])
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (0 == strcmp(ps->data[i].name, name))
{
return i;
}
}
return -1;//找不到时
}
void DelContact(struct Contact* ps)
{
int pos = 0;
char name[MAX_NAME];
printf("请输入要删除人的名字:>\n");
scanf("%s", name);
//1.查找要删除人的位置
pos = FindByName(ps, name);//找到了返回元素所在下标。找不到返回-1
//2.删除
if (pos == -1)
{
printf("要删除的人不存在\n");
}
else
{
//删除数据
int j = 0;
for (j = pos; j < ps->size; j++)
{
ps->data[j] = ps->data[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}
void SearchContact(const struct Contact* ps)
{
int pos = 0;
char name[MAX_NAME];
printf("请输入想查找人的名字:>\n");
scanf("%s", name);
pos = FindByName(ps, name);
if (pos == -1)
{
printf("要查找的人不存在\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\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)
{
int pos = 0;
char name[MAX_NAME];