目录
静态简易通讯录
代码:
功能模块展示:
设计思路:
动态简易通讯录(本质顺序表)
代码:
扩容模块展示:
设计思路:
文件版本通讯录
代码:
文件模块展示:
设计思路:
静态简易通讯录
代码:
//头文件内容
#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
//声明一个枚举类型增加主程序代码(switch部分)可读性
enum method
{
EXIT,
ADD,
DELETE,
FIND,
MODIFY,
SHOW,
DESTROY,
SORT
};
//定义标识符常量便于对数据大小修改
#define MAX_NAME 20
#define MAX_SEX 6
#define MAX_TELEPHONE 12
#define MAX_ADDRESS 20
#define Max_PEOINFO 1000
//定义一个联系人信息结构体类型
typedef struct PeoInfo
{
char name[MAX_NAME];
char sex[MAX_SEX];
int age;
char Telephone[MAX_TELEPHONE];
char address[MAX_ADDRESS];
}PeoInfo;
//创建一个联系人信息结构体数组和计数联系人个数的变量组成联系人结构体
typedef struct Contact
{
PeoInfo data[Max_PEOINFO];
int sz;
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人函数
void AddContact(Contact* pc);
//展示所有联系人函数
void ShowContact(Contact* pc);
//删除联系人函数
void DeleteContact(Contact* pc);
//查找联系人函数
void FindContact(Contact* pc);
//修改联系人函数
void ModifyContact(Contact* pc);
//清空联系人函数
void DestroyContact(Contact* pc);
//对联系人排序函数
void SortContact(Contact* pc);
//函数实现源文件内容
//初始化函数
void InitContact(Contact* pc)
{
assert(pc);
//利用库函数将整个数组所有元素初始化为0
memset(pc->data, 0, sizeof(pc->data));
//通讯录没有联系人
pc->sz = 0;
}
//增加联系人函数
void AddContact(Contact* pc)
{
assert(pc);
//通讯录已满情况
if (pc->sz == Max_PEOINFO)
{
printf("通讯录已满\n");
return;
}
//通讯录未满情况
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].Telephone);
printf("请输入联系人的地址:>");
scanf("%s", pc->data[pc->sz].address);
//每增加一个联系人,计数变量加1
pc->sz = pc->sz + 1;
printf("增加联系人成功\n");
}
//为了便于局部调试程序,先实现显示函数
//显示所有联系人
void ShowContact(Contact* pc)
{
assert(pc);
int i = 0;
//打印内容左对齐
printf("%-20s\t%-6s\t%-4s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-6s\t%-4d\t%-12s\t%-20s\n",
pc->data[i].name,
pc->data[i].sex,
pc->data[i].age,
pc->data[i].Telephone,
pc->data[i].address);
}
}
//删除联系人时我们需要知道联系人在数组中的位置(以名字删除联系人)
//名字检索函数(不需要在头文件中声明)
//此函数无法在有同名联系人时精确找到联系人
int ModifyName(char* str, Contact* pc)
{
assert(pc && str);
int i = 0;
//遍历已经存储了联系人的数组元素寻找第一个名字相符的联系人并返回下标
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(str, pc->data[i].name))
return i;
}
//找不到
return -1;
}
//删除指定联系人
//此函数无法在有同名联系人时精确删除联系人
void DeleteContact(Contact* pc)
{
assert(pc);
//以名字查找指定联系人
char name[MAX_NAME] = { 0 };
printf("请输入要删除的联系人的名字\n");
scanf("%s", name);
int num = ModifyName(name, pc);
//找到了
if (num != -1)
{
int i = 0;
for (i = num; i < pc->sz; i++)
{
memcpy(pc->data + i, pc->data + (i + 1), sizeof(PeoInfo));
}
//删除后计数变量减1
pc->sz = pc->sz - 1;
printf("删除成功\n");
}
//没找到
else
printf("删除失败,找不到指定联系人\n");
}
//查找指定联系人
//此函数无法在有同名联系人时精确查找联系人
void FindContact(Contact* pc)
{
assert(pc);
//以名字查找指定联系人
char name[MAX_NAME] = { 0 };
printf("请输入要查找的联系人的名字\n");
scanf("%s", name);
//调用上面定义的名字检索函数
int num = ModifyName(name, pc);
//找到了打印此联系人信息
if (num != -1)
{
printf("%-20s\t%-6s\t%-4s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-20s\t%-6s\t%-4d\t%-12s\t%-20s\n",
pc->data[num].name,
pc->data[num].sex,
pc->data[num].age,
pc->data[num].Telephone,
pc->data[num].address);
}
//找不到
else
printf("找不到指定联系人\n");
}
//修改指定联系人
void ModifyContact(Contact* pc)
{
assert(pc);
//以名字查找指定联系人
char name[MAX_NAME] = { 0 };
printf("请输入要修改的联系人的名字\n");
scanf("%s", name);
//调用名字检索函数
int num = ModifyName(name, pc);
//找到后重新输入此联系人信息
if (num != -1)
{
printf("请输入新的姓名\n");
scanf("%s", pc->data[num].name);
printf("请输入新的性别\n");
scanf("%s", pc->data[num].sex);
printf("请输入新的年龄\n");
scanf("%d", &(pc->data[num].age));
printf("请输入新的电话\n");
scanf("%s", pc->data[num].Telephone);
printf("请输入新的地址\n");
scanf("%s", pc->data[num].address);
printf("修改联系人成功\n");
}
//找不到
else
printf("找不到指定联系人\n");
}
//清空所有联系人
void DestroyContact(Contact* pc)
{
assert(pc);
//调用初始化函数将联系人结构体初始化
InitContact(pc);
printf("联系人已清空\n");
}
//拓展功能
//以名字对所有联系人升序排序
void SortContact(Contact* pc)
{
assert(pc);
int i = 0;
//选择排序
for (i = 0; i < pc->sz - 1; i++)
{
int j = i;
for (j = i; j < pc->sz; j++)
{
if (strcmp(pc->data[i].name, pc->data[j].name) > 0)
{
PeoInfo tmp;
memcpy(&tmp, pc->data[i].name, sizeof(PeoInfo));
memcpy(pc->data[i].name, pc->data[j].name, sizeof(PeoInfo));
memcpy(pc->data[j].name, &tmp, sizeof(PeoInfo));
}
}
}
printf("排序完成\n");
}
//主程序源文件内容
//菜单
void menu()
{
printf("****** 1.ADD 2.DELETE ********\n");
printf("****** 3.FIND 4.MODIFY ********\n");
printf("****** 5.SHOW 6.DESTROY********\n");
printf("****** 7.SORT 0.EXIT ********\n");
}
void test()
{
//定义一个通讯录结构体变量
Contact con;
//初始化
InitContact(&con);
enum method input = 0;
do
{
menu();
printf("请选择你的操作\n");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DELETE:
DeleteContact(&con);
break;
case FIND:
FindContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case DESTROY:
DestroyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
printf("程序已退出\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
功能模块展示:
增加功能:
删除功能:
查找功能:
修改功能:
拓展功能:
设计思路:
要实现一个简易版静态通讯录,无非是在实现增删查改的基础上进行进一步的功能拓展,因此设计的重点是在增删查改的功能实现上,静态版本存储通讯录内容的是定长数组,因此要实现这些功能,我们需要围绕数组下标和已经存储的联系人个数作为文章。
只要知道已经存储的联系人个数,增加联系人就会变得轻而易举。而删除,查找,修改联系人时都必须先找到要操作的联系人,因此我们需要先实现一个检索联系人接口供它们调用,此接口实现的越精确,删查改就会越精确。
为了方便对数组下标进行操作,将计数联系人个数的变量和记录联系人信息的数组都作为通讯录结构体的成员,计数变量从0开始,每增加一个联系人就加1,删除减1,这样我们就会得到通讯录的实时大小从而可以非常容易的操作通讯录,实现通讯录的功能也就变的非常简单。
动态简易通讯录(本质顺序表)
代码:
动态版本的代码只需在静态版本上稍作修改即可。
//头文件增加和修改的内容
//初始容量
#define INIT_SZ 3
//每次扩容增加容量
#define ADD_SZ 2
//修改后通讯录结构体
typedef struct Contact
{
//PeoInfo data[Max_PEOINFO];
//用结构体指针代替结构体数组
PeoInfo* data;
int sz;
//增加一个记录通讯录容量的变量
int capacity;
}Contact;
//函数实现源文件增加和修改的内容
//动态版本初始化函数
void InitContact(Contact* pc)
{
assert(pc);
//先开辟INIT_SZ个空间
pc->data = (PeoInfo*)malloc(INIT_SZ * sizeof(PeoInfo));
//开辟失败
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->sz = 0;
//记录容量
pc->capacity = INIT_SZ;
}
//增加联系人时需要先检查容量是否已满,若满则增加容量
//空间动态增长(无需在头文件中声明)
int mallocContact(Contact* pc)
{
assert(pc);
//已满
if (pc->sz == pc->capacity)
{
//扩容
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + ADD_SZ) * sizeof(PeoInfo));
//扩容失败
if (ptr == NULL)
{
perror("mallocContact");
return 0;
}
//扩容成功
else
{
pc->data = ptr;
pc->capacity += ADD_SZ;
printf("扩容成功\n");
return 1;
}
}
//未满
return 1;
}
//动态版本增加函数
void AddContact(Contact* pc)
{
assert(pc);
//扩容失败
if (0 == mallocContact(pc))
{
return;
}
//未满或扩容成功
printf("请输入联系人的姓名\n");
scanf("%s", pc->data[pc->sz].name);
printf("请输入联系人的性别\n");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入联系人的年龄\n");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入联系人的电话\n");
scanf("%s", pc->data[pc->sz].Telephone);
printf("请输入联系人的地址\n");
scanf("%s", pc->data[pc->sz].address);
pc->sz = pc->sz + 1;
printf("增加联系人成功\n");
}
//动态版本清空函数
void DestroyContact(Contact* pc)
{
assert(pc);
free(pc->data);
pc->data = NULL;
pc->sz = 0;
pc->capacity = 0;
printf("联系人已清空\n");
}
扩容模块展示:
设计思路:
静态版本的通讯录十分死板,需要存储的数据少了就会存在空间浪费,多了又会不够,因此衍生出动态版本来解决这一问题,当存储空间不够时就向内存申请新的空间来增加容量,更加灵活。
为了实现实时向内存申请空间,需要一个变量用来记录通讯录结构体的容量,当存储的联系人个数等于容量时就需要进行扩容,同时由于通讯录的大小是动态变化的,因此用结构体指针替代结构体数组来维护存储空间。
由于每次扩容后的存储空间依然是连续的,因此在没有发生访问越界时可以像操作数组一样操作指针,静态版本的大多数代码都无需修改。
文件版本通讯录
代码:
在动态版本上稍作修改。
//函数实现源文件增加和修改的内容
//容量检查函数声明
int mallocContact(Contact* pc);
//初始化时需要先读取文件contact.dat的内容到data中去
void LoadContact(Contact* pc)
{
//打开文件
FILE* pf = fopen("contact.dat", "rb");
//若路径下没有此文件,则打开文件失败
if (pf == NULL)
{
perror("LoadContact");
return;
}
PeoInfo tmp = { 0 };
//读文件
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
//扩容判断
if (0 == mallocContact(pc))
return;
pc->data[pc->sz] = tmp;
//每读取一个计数变量加1
pc->sz++;
}
//关闭文件
fclose(pf);
pf = NULL;
}
//动态版本初始化函数
void InitContact(Contact* pc)
{
assert(pc);
pc->data = (PeoInfo*)malloc(INIT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->sz = 0;
pc->capacity = INIT_SZ;
//载入文件内容
LoadContact(pc);
}
//将通讯录信息以二进制形式保存到contact.dat文件中
void SaveContact(Contact* pc)
{
//打开文件(若路径下没有此文件,则新建一个此文件)
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL)
{
perror("SaveContact");
return;
}
int i = 0;
for (i = 0; i < pc->sz; i++)
{
//写入文件
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
//关闭文件
fclose(pf);
pf == NULL;
printf("通讯录内容已保存到contact.dat文件中\n");
}
//主程序源文件修改内容
case EXIT:
//在退出程序时将内存中的信息保存到文件中去
SaveContact(&con);
printf("程序已退出\n");
break;
文件模块展示:
设计思路:
无论是静态版本还是动态版本,联系人信息都是存储在内存中的,一旦程序退出运行,这些信息也会随之消失,为了长久存储这些信息,衍生出了文件版本的通讯录,即利用文件操作函数将存储在内存中的信息存储到文件中长久保存。
我们只需要在动态版本的基础上增加一个读取文件函数和写入文件函数,在程序开始时先将文件中的信息读取到内存中,在程序退出时再将内存中的数据写回文件中即可。