目录
1.缕清实现思路
实现一个通讯录;
通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
(容量这里使用动态内存实现)
可执行操作:
add 添加联系人信息
delete 删除指定联系人信息
search 查找指定联系人信息
modify 修改指定联系人信息
show 显示所有联系人信息
clear 清空所有联系人
sort 以名字排序所有联系人
这里动态实现的通讯录本质上是一种动态顺序表
2. 代码实现
操作指引部分使用枚举将输入于相应操作一一列举
增加代码可读性
enum option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
CLEAR
};
自定义结构体类型,存储联系人信息
typedef struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
typedef struct Contact
{
PeoInfo* data;
int sz;//通讯录中有效信息的个数
int capacity; //通讯录的容量
}Contact;
1.1 通讯录初始化,读取文件
- 使用malloc申请动态内存空间,创建通讯录
- 同时将已有文件中的数据以二进制读取到通讯录中
- 每次将文件中的一个PeoInfo结构体的数据以二进制读取到临时变量中
- 再将其赋给通讯录中的数据项
//将已有文件中的数据以二进制读取到通讯录中
void LoadContact(Contact* pc)
{
FILE* pf = fopen("contact.dat", "rb");
if (pf == NULL)
{
perror("fopen");
return;
}
PeoInfo tmp = { 0 };
//每次将文件中的一个PeoInfo结构体的数据以二进制读取到临时变量中
//再将其赋给通讯录中的数据项
while (fread(&tmp, sizeof(PeoInfo), 1, pf))
{
CheckCapacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
fclose(pf);
pf = NULL;
}
//使用malloc申请动态内存空间,创建通讯录
void InitContact(Contact* pc)
{
pc->data = (PeoInfo*)malloc(DEFAULT_SIZE * sizeof(PeoInfo));
if (pc->data == NULL)
{
printf("malloc fail\n");
exit(-1);
}
else
{
pc->sz = 0;
pc->capacity = DEFAULT_SIZE;
}
LoadContact(pc);
}
1.2 添加联系人
当初始化的空间不够时,要进行扩容,这里定义CheckCapacity
函数实现
每次增加联系人时都首先检查容量,如果已满则每次增加INC_SIZE
void CheckCapacity(Contact* pc)
{
if (pc->sz == pc->capacity)
{
int newcapacity = pc->capacity + INC_SIZE;
PeoInfo* tmp = (PeoInfo*)realloc(pc->data, newcapacity * sizeof(PeoInfo));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
pc->data = tmp;
pc->capacity = newcapacity;
printf("增容成功,当前容量:%d\n", newcapacity);
}
}
}
一段输入输出函数获取需存储的数据
void AddContact(Contact* pc)
{
CheckCapacity(pc);
printf("请输入姓名->");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄->");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入性别->");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话->");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入地址->");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
}
1.3 打印通讯录
为使打印出的通讯录各列对齐,通过格式控制字符串调整对齐方式和域宽
void ShowContact(Contact* pc)
{
printf("%-20s %-5s %-5s %-12s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%-20s %-5d %-5s %-12s %-20s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
1.4 删除联系人
通过输入判断删除项,封装一个FindByName
函数通过输入的姓名返回其所处位置下标
FindByName
后续操作也会经常用到
删除操作时,待删除位置的后数据以此前移一位即可实现删除
int FindByName(Contact* pc, char* str)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, str) == 0)
{
return i;//找到返回位置下标
}
}
return -1;//找不到返回-1
}
void DeletContact(Contact* pc)
{
char str[MAX_NAME] = { 0 };
printf("请输入需删除的联系人姓名->");
scanf("%s", str);
int pos = FindByName(pc, str);
while (pos < pc->sz - 1)
{
//待删除位置的后数据以此前移一位即可实现删除
pc->data[pos] = pc->data[pos + 1];
pos++;
}
pc->sz--;
}
1.5 查找联系人
同样使用FindByName
返回查找位置的下标
同时将查找的信息打印
void SearchContact(Contact* pc)
{
char str[MAX_NAME] = { 0 };
printf("请输入需查找的联系人姓名->");
scanf("%s", str);
int pos = FindByName(pc, str);
printf("%-20s %-5s %-5s %-12s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s %-5d %-5s %-12s %-20s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
1.6 修改联系人
一段又臭又长的输入输出函数
void ModifyContact(Contact* pc)
{
char str[MAX_NAME] = { 0 };
printf("请输入需修改的联系人姓名->");
scanf("%s", str);
int pos = FindByName(pc, str);
printf("请输入姓名->");
scanf("%s", pc->data[pos].name);
printf("请输入年龄->");
scanf("%d", &(pc->data[pos].age));
printf("请输入性别->");
scanf("%s", pc->data[pos].sex);
printf("请输入电话->");
scanf("%s", pc->data[pos].tele);
printf("请输入地址->");
scanf("%s", pc->data[pos].addr);
printf("修改成功\n");
ShowContact(pc);
}
1.7 通讯录排序
使用qsort
函数根据姓名进行排序,注意比较函数中的指针类型转换为(PeoInfo*)
int cmp_by_name(const void* p1, const void* p2)
{
return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
printf("排序成功\n");
ShowContact(pc);
}
1.8 将通讯录写入文件
- 清空通讯录前将其写入文件保存起来
- 循环每次将
pc->data + i
PeoInfo 结构体以二进制写入文件流
void SaveContact(Contact* pc)
{
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL)
{
perror("fopen");
return;
}
//一次将 pc->data + i 结构体以二进制写入文件流
for (int i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
}
fclose(pf);
pf = NULL;
printf("保存成功\n");
}
1.9 清空通讯录
清空时释放动态内存空间
void ClearContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->sz = 0;
pc->capacity = 0;
printf("通讯录已清空\n");
}
2. 总结
这里的通讯录实际上是一种链表,比如其中的增加联系人属于尾插接口函数