实现一个通讯录,可以用来存储1000个人的信息。
每个人的信息包括:姓名、性别、年龄、电话、住址。
提供方法:
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 清空所有联系人
- 以名字排序所有联系人
- 添加一个函数,在退出通讯录的时候把信息到保存到文件中
- 添加一个函数,在通讯录打开的时候,可以把文件中的信息加载到通讯录中
通讯录简介:
运行程序后,可以进入选择界面,通过输入菜单中的序号来选择相应的操作,如下图:
添加联系人信息,如下图:
添加完联系人信息后输入5,显示所有联系人信息,如下图:
输入3查找联系人,如下图:
输入2删除指定的联系人,如下图:
输入4修改指定的联系人,如下图:
持久化存储:在增加两个联系人信息后,选择0退出,然后重新编译运行,选择5 显示联系人信息,就可以看到上一次退出前保存的联系人信息。
编写过程:
- 建立三个文件,头文件 contact.h 用于函数声明等;源文件contact.c 存放自定义函数;源文件main.c 存放主函数,组织整个程序框架。
- 在main.c中写一个main()函数,作为程序运行的入口,将菜单函数Menu()放到里面。
int main()
{
Menu();
system("pause");
return 0;
} - 写一个菜单函数,用于打印通讯录的菜单。
static void Menu()
{
printf("###############################################\n");
printf("# 1.Add 2.Delete #\n");
printf("# 3.Search 4.Modify #\n");
printf("# 5.Show 6.Clear #\n");
printf("# 7.Sort 0.Exit #\n");
printf("###############################################\n");
} - 我们定义一个结构体,命名为person_t,用来存储联系人的信息;定义一个结构体,命名为contact_t,用来存放动态分配的内存容量以及联系人的数量。
typedef struct person //描述人的信息的 结构体
{
char name[32];
char sex[8];
int age;
char telephone[16];
char address[64];
}person_t;
typedef struct contact //通讯录类型
{
int cap; //当前总容量 如题中要容纳1000人
int size; //有效人数
person_t persons[0]; //柔性数组
}contact_t; - 在mian.c中写一个函数main(),该函数用于将其他函数组织起来,成为完整的通讯录运行函数。
int main()
{
int quit = 0;
int select = 0;
while (!quit)
{
Menu();
scanf("%d", &select);
switch (select)
{
case 1:
AddPerson(&ct);//考虑空间问题
break;
case 2:
DeletePerson(ct);
break;
case 3:
SearchPerson(ct);
break;
case 4:
ModPerson(ct);
break;
case 5:
PrintContact(ct);
break;
case 6:
ClearContact(ct);
break;
case 7:
SortContact(ct);
break;
case 0:
quit = 1;
break;
default:
printf("您输入的值有误,请重新输入!\n");
break;
}
}
printf("ByeBye!\n");
system("pause");
return 0;
} -
子函数简介: InitContact( )函数 申请空间,初始化通讯录。这个函数通过调用malloc( )函数动态分配两个起始空间通信录和人。 AddPerson( )函数 添加联系人信息。当用户输入1后,main( )函数调用该函数,让用户添加一个联系人,这个函数会调用 IsFull() 函数判断空间是否满,若空间已满,调用?Inc() 函数增容。 DeletePerson( )函数 删除指定联系人信息 。当用户输入2后,main() 函数调用该函数,该函数将会提示用户输入要删除的联系人的姓名,当用户输入姓名后,该函数会调用FindPerson( )函数进行查找。此时该函数会提示用户输入要查找的联系人姓名,这样就可以删除输入联系人的所有信息了。 SearchPerson( )函数 查找指定联系人信息,并将其打印。当用户输入3后,main() 函数将调用该函数,该函数会提示用户输入要查找的联系人的姓名,当用户输入姓名后,该函数将调用Find( )函数进行查找,并将找到的联系人信息打印出来。 ModPerson( )函数 修改指定联系人信息。当用户输入4后,main() 函数将调用该函数,该函数会提示用户输入要修改的用户的姓名,然后通过用户输入信息对原变量重新赋值,达到修改信息的目的。 PrintContact( )函数 显示所有联系人信息 。当用户输入5后,main() 函数调用该函数,将通讯录中所有的联系人信息打印出来。 ClearContact( )函数 清空所有联系人。当用户输入6后,main() 函数调用该函数,该函数将释放之前动态分配的所有空间,并将联系人计数变量和内存计数变量都清空。 SortContact( )函数 按名字排序所有的联系人。当用户输入7后,main() 函数将调用该函数,该函数会通过使用qsort( )函数对联系人信息(persons_t类型的结构体变量)按姓名进行排序。 SaveFile( )函数 将通讯录的内容存入文件中。当用户输入0打算退出的时候,main( )函数将调用该函数,该函数通过文件二进制写操作将persons_t类型的数组中的数据(联系人信息)保存到contact.bin文件中。 IsFull( )函数 判断空间是否已满,返回值为1,表示空间已满,返回值为0,表示空间未满。 FindPerson(?)函数 按名字查找联系人,使用strcmp字符串比较函数对联系人进行比较。 Inc( )函数 扩容函数,其方法是使用realloc( )函数重新分配一块更大的内存空间,扩容成功,更新地址。
通讯录代码总览:
contact.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define DFL_NUM 3
#define INC_NUM 1 //一次扩容的大小
#define SAVE_FILE "contact.bin" //以二进制形式保存 方便恢复
typedef struct person //描述人的信息的 结构体
{
char name[32];
char sex[8];
int age;
char telephone[16];
char address[64];
}person_t;
typedef struct contact //通讯录类型
{
int cap; //当前总容量 如题中要容纳1000人
int size; //有效人数
person_t persons[0]; //柔性数组
}contact_t;
extern void LoadFile();
extern void InitContact(contact_t** ctp);
extern void DestoryContact(contact_t** ctp);
//提供方法
extern void AddPerson(contact_t **ctp); //1.添加联系人信息
extern void DeletePerson(contact_t* ct); //2.删除指定联系人信息
extern void SearchPerson(contact_t* ct); //3.查找指定联系人信息
extern void ModPerson(contact_t* ct); //4.修改指定联系人信息
extern void PrintContact(contact_t* ct); //5.显示所有联系人信息
extern void ClearContact(contact_t* ct); //6.清空所有联系人
extern void SortContact(contact_t* ct); //7.以名字排序所有联系人
extern void SaveFile(const contact_t* ct);
contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void LoadFile()
{
}
void InitContact(contact_t** ctp) //申请空间
{
FILE* fp = fopen(SAVE_FILE, "rb");
if (NULL == fp)
{
//默认初始化
*ctp = (contact_t*)malloc(sizeof(contact_t) + sizeof(person_t) * DFL_NUM); //申请堆空间 通讯录和人
if (NULL == *ctp) //申请空间失败
{
perror("malloc"); //打印报错
exit(1); //程序中止
}
(*ctp)->cap = DFL_NUM;
(*ctp)->size = 0;
printf("default Contace init … done\n");
}
else
{
//文件方案
contact_t t;
fread(&t, sizeof(contact_t), 1, fp);
*ctp = (contact_t*)malloc(sizeof(contact_t) + sizeof(person_t) * t.cap);
if (NULL == *ctp)
{
perror("malloc");
exit(2);
}
fread((*ctp)->persons, sizeof(person_t), t.size, fp);
(*ctp)->cap = t.cap;
(*ctp)->size = t.size;
fclose(fp);
printf("file Contact init ... done\n");
}
}
static int IsFull(contact_t* ct) //判断空间是否满
{
return ct->size == ct->cap; //返回值 1表示已满 0表示未满
}
static Inc(contact_t **ctp) //增容 成功:1 失败:0
{
contact_t* ct = realloc(*ctp, sizeof(contact_t) + sizeof(person_t) * ((*ctp)->cap + INC_NUM));//原来柔性数组的空间大小+扩容的空间大小
if (NULL == ct) //若为空 扩容失败
{
perror("realloc");//打印报错
return 0;
}
//增容成功
ct->cap = (*ctp)->cap + INC_NUM; //更新调整空间
ct->size = (*ctp)->size;
*ctp = ct; //更新地址
printf("扩容成功,cap:%d, size:%d\n", ct->cap, ct->size);
return 1;
}
static int FindPerson(const contact_t* ct, const char* name) //按名字查找联系人
{
const person_t* p = ct->persons;
for (int i=0; i < ct->size; i++)
{
if (strcmp(p[i].name, name) == 0) //已找到
//strcmp字符串比较函数
//int strcmp(const char *s1,const char *s2);
//如果返回值 < 0,则表示 s1 小于 s2。
//如果返回值 > 0,则表示 s1 大于 s2。
//如果返回值 = 0,则表示 s1 等于 s2。
{
return i;
}
}
return -1;//未找到所要查找的联系人
}
static PrintContactHelper(const person_t* p) //格式化打印
{
printf("|%-10s|%-4s|%-3d|%8s|%13s|\n", p->name, p->sex, p->age, p->telephone, p->address);
}
static int CompareName(const void* xp, const void* yp) //比较名字 完成排序
{
const person_t* p = (const person_t*)xp;
const person_t* q = (const person_t*)yp;
return strcmp(p->name, q->name); //字符串比较
}
void AddPerson(contact_t **ctp) // 1.增加联系人 考虑空间问题
{
//空间已满且扩容失败
//想自动扩容
if (!IsFull(*ctp) || Inc(ctp))
{//空间未满 直接进来;空间已满 扩容成功
contact_t* ct = *ctp;
person_t* p = ct->persons + ct->size;
printf("请输入你的名字# ");
scanf(" %s",p->name);
if (FindPerson(*ctp, p->name) >= 0) {
printf("[%s]已经存在!\n", p->name);
return;
}
printf("请输入你的性别# ");
scanf(" %s",p->sex);
printf("请输入你的年龄# ");
scanf(" %d", &(p->age) );
printf("请输入你的电话# ");
scanf(" %s",p->telephone);
printf("请输入你的地址# ");
scanf(" %s",p->address);
ct->size++;
}
}
void DeletePerson(contact_t* ct) // 2.删除指定联系人信息
{
char name[32] = { 0 };
printf("请输入要查找的联系人的姓名#");
scanf("%s", name);
int index = FindPerson(ct, name); //查找联系人
person_t* p = ct->persons;
if (index < 0) //返回值为 -1
{
printf("查无[%s]联系人!\n", name);
return;
}
memmove(ct->persons + index, ct->persons + ct->size - 1, sizeof(person_t));
//void *memmove(void *dst, const void *src, size_t count);
//最后一个联系人的信息拷贝到要删除的联系人信息里边,完成对指定联系人信息的删除功能
ct->size--;
printf("删除[%s]联系人成功\n", name);
}
void SearchPerson(contact_t* ct) // 3.查找指定联系人信息
{
char name[32] = { 0 };
printf("请输入要查找的人姓名#");
scanf("%s", name);
int index = FindPerson(ct, name);
if (index < 0) //返回值为 -1
{
printf("查无[%s]联系人!\n", name);
return;
}
PrintContactHelper(ct->persons + index);
}
void ModPerson(contact_t* ct) // 4.修改指定联系人信息
{
char name[32] = { 0 };
printf("请输入要查找的联系人的姓名#");
scanf("%s", name);
int index = FindPerson(ct, name);
person_t* p = ct->persons;
if (index < 0) //返回值为 -1
{
printf("查无[%s]联系人!\n", name);
}
else
{
printf("姓名修改为#");
scanf(" %s",p->name);
printf("性别修改为#");
scanf(" %s",p->sex);
printf("年龄修改为#");
scanf(" %d",&(p->age));//这里需要注意一下:记得取地址
printf("电话修改为#");
scanf(" %s",p->telephone);
printf("地址修改为# ");
scanf(" %s",p->address);
printf("修改成功!\n");
}
}
void PrintContact(contact_t* ct) // 5.显示联系人信息
{
for (int i = 0; i < ct->size; i++)
{
PrintContactHelper(ct->persons + i);
}
}
void ClearContact(contact_t* ct) // 6.清空所有联系人
{
printf("Clear ... done\n");
ct->size = 0;
}
void SortContact(contact_t* ct) // 7.以名字排序所有联系人
{
qsort(ct->persons, ct->size, sizeof(person_t), CompareName);
}
void SaveFile(const contact_t* ct)
{
FILE* fp = fopen(SAVE_FILE, "wb");//二进制形式打开SAVE_FILE文件
if (NULL == fp)
{
perror("fopen");
return;
}
fwrite(ct, sizeof(contact_t), 1, fp);
fwrite(ct->persons, sizeof(person_t), ct->size, fp);
//size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE * stream)
// 参数
// ptr --指向要被写入的元素数组的指针。
// size --要被写入的每个元素的大小,以字节为单位。
// nmemb -- 元素的个数,每个元素的大小为 size 字节。
// stream -- 指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
// 返回值
// 如果成功,该函数返回一个 size_t 对象,表示元素的总数,该对象是一个整型数据类型。如果该数字与 nmemb 参数不同,则会显示一个错误。
fclose(fp);
}
void DestoryContact(contact_t **ctp)
{
free(*ctp);
*ctp = NULL;
printf("Contace destory … done\n");
}
main,c
#include "contact.h"
static void Menu() //只能在本文件内使用
{
printf("###############################################\n");
printf("# 1.Add 2.Delete #\n");
printf("# 3.Search 4.Modify #\n");
printf("# 5.Show 6.Clear #\n");
printf("# 7.Sort 0.Exit #\n");
printf("###############################################\n");
printf("Please Select# ");
}
int main()
{
contact_t* ct = NULL; //创建通讯录 申请堆空间
InitContact(&ct); //初始化一个通讯录
int quit = 0;
int select = 0;
while (!quit)
{
Menu();
scanf("%d", &select);
switch (select)
{
case 1:
AddPerson(&ct);//考虑空间问题
break;
case 2:
DeletePerson(ct);
break;
case 3:
SearchPerson(ct);
break;
case 4:
ModPerson(ct);
break;
case 5:
PrintContact(ct);
break;
case 6:
ClearContact(ct);
break;
case 7:
SortContact(ct);
break;
case 0:
quit = 1;
SaveFile(ct);
break;
default:
printf("您输入的值有误,请重新输入!\n");
break;
}
}
DestoryContact(&ct); //释放
printf("ByeBye!\n");
system("pause");
return 0;
}
|