目录
前言
制作这个通讯录主要用到了以自定义类型(结构体、枚举类型)、文件操作、指针、动态内存分配的知识,代码共计300行左右
通讯录实现了动态容量以及保存功能
代码一览
test.c文件:(主函数所在)
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void menu()
{
printf("*******************\n");
printf("****1.Add 2.Del*******\n");
printf("****3.Search 4.Modify****\n");
printf("****5.Show 6.Clear*****\n");
printf("****7.Sort 0.Exit******\n");
}
int main()
{
int input = 0;
contact con;
Initcontact(&con);//初始化通讯录
do
{
menu();
printf("请选择->");
scanf("%d", &input);
switch (input)
{
case Exit:
printf("退出通讯录\n");
SaveContact(&con);
printf("通讯录保存成功\n");
break;
case Add://添加联系人
AddContact(&con);
break;
case Del://删除联系人
DelContact(&con);
break;
case Sear://查找联系人
printf("请输入要查找联系人的姓名\n");
char arr[20] = { 0 };
scanf("%s", arr);
int a = Search(&con, arr);
printf("找到了:\n");
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\t\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-10s\t%-5s\t%-5d\t%-12s\t%-20s\t\n", con.data[a].name,
con.data[a].sex, con.data[a].age, con.data[a].tele,con.data[a].addr);
break;
case Modify://修改通讯录
ModifyContact(&con);
break;
case Show://展示通讯录
ShowContact(&con);
break;
case Clear://重置通讯录
Initcontact(&con);
break;
case Sort:
SortContact(&con);//排序
break;
default:
break;
}
} while (input);
return 0;
}
contact.c文件(功能的实现)
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void LoadContact(contact* pa)
{
people tmp = { 0 };
FILE* pf = fopen("contact.dat", "rb");
if (pf == NULL)
{
printf("LoadContact::%s", strerror(errno));
return;
}
while (fread(&tmp, sizeof(people), 1, pf))
{
CheckCapacity(pa);
pa->data[pa->sz] = tmp;
pa->sz++;
}
fclose(pf);
pf = NULL;
}
void SaveContact(contact* pa)
{
assert(pa);
int i = 0;
FILE* pf = fopen("contact.dat", "wb");
if (pf == NULL)
{
printf("SaveContact::%s", strerror(errno));
return;
}
for (i = 0; i < pa->sz; i++)
{
fwrite(pa->data + i, sizeof(people), 1, pf);
}
fclose(pf);
pf = NULL;
}
void Initcontact(contact* pa)
{
assert(pa);
pa->sz = 0;
pa->data = (people*)calloc(3,sizeof(people));
pa->max = 3;
LoadContact(pa);//加载上次保存过的通讯录
}
void CheckCapacity(contact* pa)
{
if (pa->sz == pa->max)
{
people* ret = (people*)realloc(pa->data, pa->max + 1);
if (ret != NULL)
{
printf("增容成功\n");
pa->data = ret;
pa->max++;
}
else
{
printf("增容失败\n");
return;
}
}
}
void AddContact(contact* pa)
{
assert(pa);
CheckCapacity(pa);
printf("请输入名字\n");
scanf("%s", pa->data[pa->sz].name);
printf("请输入年龄\n");
scanf("%d", &pa->data[pa->sz].age);
printf("请输入性别\n");
scanf("%s", pa->data[pa->sz].sex);
printf("请输入电话\n");
scanf("%s", pa->data[pa->sz].tele);
printf("请输入地址\n");
scanf("%s", pa->data[pa->sz].addr);
pa->sz++;
printf("增加联系人成功\n目前有%d名联系人\n",pa->sz);
}
void ShowContact(const contact* pa)
{
assert(pa);
if (pa->sz == 0)
{
printf("目前没有联系人\n");
return;
}
int i = 0;
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\t\n", "姓名", "性别", "年龄", "电话", "地址");
for(i=0;i<pa->sz;i++)
printf("%-10s\t%-5s\t%-5d\t%-12s\t%-20s\t\n", pa->data[i].name,
pa->data[i].sex, pa->data[i].age, pa->data[i].tele, pa->data[i].addr);
}
int Search(const contact* pa,const char* pb)
{
assert(pa&&pb);
int i = 0;
for (i = 0; i < pa->sz; i++)
{
if (strcmp(pb, pa->data[i].name) == 0)
return i;
}
return -1;
}
void DelContact(contact* pa)
{
assert(pa);
if (pa->sz == 0)
{
printf("无联系人可删除\n");
return;
}
char arr[20] = { 0 };
printf("请输入要删除的联系人的姓名\n");
scanf("%s", arr);
int a=Search(pa, arr);
if (a == -1)
{
printf("要删除的联系人不在\n");
}
else
{
int j = 0;
for (j = a; j < pa->sz - 1; j++)
{
pa->data[j] = pa->data[j + 1];
}
printf("联系人删除成功\n");
pa->sz--;
}
}
void ModifyContact(contact* pa)
{
printf("请输入要修改的联系人的姓名\n");
char arr[20] = { 0 };
scanf("%s", arr);
int i = Search(pa, arr);
printf("请输入要修改的名字\n");
scanf("%s", pa->data[i].name);
printf("请输入要修改的年龄\n");
scanf("%d", &pa->data[i].age);
printf("请输入要修改的性别\n");
scanf("%s", pa->data[i].sex);
printf("请输入要修改的电话\n");
scanf("%s", pa->data[i].tele);
printf("请输入要修改的地址\n");
scanf("%s", pa->data[i].addr);
printf("修改成功\n");
}
int cmp_byname(const people* e1, const people* e2)
{
if (strcmp(e1->name, e2->name) > 0)
return 1;
else if (strcmp(e1->name, e2->name) < 0)
return -1;
else
return 0;
}
void SortContact(contact* pa)
{
assert(pa);
if (pa->sz == 0)
{
printf("无联系人,无法排序\n");
return;
}
int(*cmp)(const people * e1, const people * e2)=cmp_byname;
qsort(pa->data, pa->sz, sizeof(pa->data[0]), cmp);
printf("已将联系人列表按姓名排序\n");
}
contact.h文件(函数的声明,枚举类型的创建以及结构体的重命名,各种头文件包含)
#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#define name_max 20
#define sex_max 5
#define addr_max 20
#define tele_max 12
#define MAX 1000
enum Contact
{
Exit,
Add,
Del,
Sear,
Modify,
Show,
Clear,
Sort
};
typedef struct people
{
char name[name_max];
int age;
char sex[sex_max];
char addr[addr_max];
char tele[tele_max];
}people;
typedef struct contact
{
people* data;
int sz;
int max;
}contact;
void Initcontact(contact* pa);
void AddContact(contact* pa);
void ShowContact(contact* pa);
int Search(contact* pa,char* pb);
void DelContact(contact* pa);
void ModifyContact(contact* pa);
void SortContact(contact* pa);
void LoadContact(contact* pa);
void SaveContact(contact* pa);
void CheckCapacity(contact* pa);
代码剖析
下面一步一步剖析代码写出来时的思路
首先肯定要创建结构体,成员变量分别为姓名,年龄,性别,地址以及电话,这里还用到了define定义常量
这里再创建一个结构体,其中data(结构体指针)用于存储上面这个结构体的内容,sz用于记录现在通讯录中有多少人,max记录通讯录最大容量
准备工作完成,就开始初始化通讯录
这里assert用于断言,如果pa是空指针,也就是创建的结构体变量的地址不在,就无法编译程序,初始化通讯录的现有人数为0,最大容量为3。用calloc而不是malloc的好处是可以直接把创建的动态数组全部初始化为0,这里最后一步的加载在下面会解释
接下来简单的打印菜单,用户输入选择功能,同时创建枚举类型,使代码可读性提高
选择1进入添加联系人功能
这里第二行的CheckCapacity用于检测当前通讯录的容量是否可以添加联系人,如果不够则增加容量,增容时用realloc函数(realloc函数用法)
选择2进入删除联系人功能
首先进行判断,如果通讯录中没有联系人则进行提示,删除时首先输入要删除联系人的姓名,然后根据姓名进行查找(该函数在下一个进行解释,此处只需知道Search函数返回对应要删除联系人在通讯录数组中的下标),若找不到该联系人则进行提示,若能找到要删除的联系人,则从该联系人的下标开始从前往后覆盖数据进行删除(不是真正的把这个人直接进行删除操作,这样会造成数组中有一个位置不存储联系人),删除完后记得让联系人数量-1
查找联系人函数比较简单,遍历通讯录,用strcmp函数进行姓名的判断,如果姓名相同则返回该联系人在通讯录数组中的下标,若没找到则返回-1(这里默认通讯录中不会出现姓名相同的情况)
选择3进入查找联系人功能,主体仍为查找联系人函数,如果找到了将联系人的相关信息进行打印
比较有意思的是这里标题栏的打印,经过调整在我的电脑上是这样的效果,感觉还比较美观
选择4进入修改联系人功能
还是以查找联系人函数为主题,找到后将数组中该下标位置的联系人信息修改即可,比较简单
选择5将目前通讯录打印
还是比较简单哈哈哈哈哈哈,for循环打印数组内容即可,这里标题栏的打印和上面的查找是一样的
选择6重置通讯录,这里还是用了初始化通讯录的函数,如果要重置通讯录选择否即可
效果如下
选择7进入排序功能
因为是根据姓名的结构体进行排序,这里直接用了C语言自带的qsort函数,也可以用快排等排序进行排序
qsort的使用需要自己写比较函数并以函数指针做参数qsort使用
接下来就是退出通讯录时的保存和再次进入通讯录时的加载功能
这里用到文件操作,用fopen函数以二进制写入方式打开contact.dat文件,如果没有这个文件则会在项目目录下创建该文件并返回该文件的文件指针,若打开失败返回NULL指针,接下来用strerror函数判断错误原因strerror用法,打开成功后再用fwrite将联系人一个一个地以二进制形式写入pf指向的文件,最后fclose关闭文件并将pf制成空指针
最后来看铺垫已久的加载功能,这次以二进制读取的方式打开文件,再用fread写入到tmp结构体中,再通过结构体之间的赋值将文件中的数据一个个加载到通讯录数组中,注意这里加载前也要先判断一下容量
总结
源码下载:C-learning: 一个小白的仓库 - Gitee.com
摸鱼了摸鱼了!!