用C语言实现一个简单通讯录功能:
- 命令端输入对应指令可执行相应操作
- 可以实现增、删、查、改、排序、显示、一键清除等功能
- 每次输入的信息保存到txt文件中
- 每次打开通讯录自动加载已有的txt文件中的信息
本文主要是针对指针、动态内存管理、以及文件读写操作的综合练习。
目录
DEL ALL一键删除,SaveCon保存通讯录,Distory释放内存:
- //通讯录结构体
- typedef struct Date
- {
- char name[20];
- char sex[5];
- int age;
- char tel[20];
- char address[30];
- }Date;
-
- typedef struct Con
- {
- Date* p;
- int count;
- int capacity;
- }Con;
其中:
声明完结构体后,先构建程序框架
- //Con.h
- #include
- //main.c
- void menu()
- {
- printf("******************************************\n");
- printf("******** 1.ADD 2.DEL ******\n");
- printf("******** 3.SHOW 4.MODIFY ******\n");
- printf("******** 5.CHECK 6.DEL ALL ******\n");
- printf("******** 7.SORT 0.EXIT ******\n");
- printf("******************************************\n");
-
- }
- int main()
- {
- int input = 0;
- Con arr;
- do
- {
- menu();
- scanf("%d", &input);
- } while (input);
- return 0;
- }
其中:
完善框架:
只针对于上述要求,我们需要创建以下函数:
InitCon:初始化通讯录,开辟内存
LoadCon:加载txt中的信息到内存中
ADD,DEL,CHECK,MODIFY:对应增、删、查、改
SHOW:显示全部信息
SORT:对某一项进行排序
DEL ALL:一键清除所有信息
EXIT:退出并保存
Distory:释放内存
完善后的主函数如下:
- int main()
- {
- int input = 0;
- Con arr;
- InitCon(&arr);
- LoadCon(&arr);
- void (*pf[])(Con*) = {0,add,del,show,modify,check,del_all,sort};
- do
- {
- menu();
- scanf("%d", &input);
- if (input == 0)
- {
- SaveCon(&arr);
- Distory(&arr);
- printf("退出!\n");
- }
- else if (input > 0 && input < 8)
- {
-
- pf[input](&arr);
- }
- else
- printf("输入错误,请重新输入!\n");
-
- } while (input);
- return 0;
- }
其中:
为了日后的维护与管理,所有函数均存入到Con.c中,与主函数分开。
在程序开始时开辟指定大小的内存,在动态规划中有malloc和calloc两种方式开辟内存,这里使用calloc的形式进行开辟。
- //Con.h
- #include
- #include
- #include
-
- #define INIT_NUM 3
-
- //Con.c
- void InitCon(Con* pc)
- {
- assert(pc);
- pc->p = (Date*)calloc(INIT_NUM, sizeof(Date));
- if (pc->p == NULL)
- {
- printf("InitCon::%s", strerror(errno));
- return;
- }
- pc->count = 0;
- pc->capacity = INIT_NUM;
- }
其中:
这里用到了文件指针,对文件进行操作,大致思想为:
- void LoadCon(Con* pc)
- {
- assert(pc);
- FILE* pfopen = fopen("con.txt", "r");
- if (pfopen == NULL)
- {
- perror("LoadCon");
- return;
- }
- Date tmp = { 0 };
- while (fread(&tmp, sizeof(Date), 1, pfopen)==1)
- {
- AddCapacity(pc);
- pc->p[pc->count] = tmp;
- pc->count++;
- }
- printf("加载成功!\n");
- fclose(pfopen);
- pfopen = NULL;
- }
其中:
扩容函数需要完成两个任务:
1、检查内存是否足够
2、内存不足时进行扩容,并将capacity中存储的信息更新
- void AddCapacity(Con* pc)
- {
- assert(pc);
- if (pc->count == pc->capacity)
- {
- Date* str = (Date*)realloc(pc->p, sizeof(Date)*(pc->capacity+ ADD_NUM));
- if (pc->p == NULL)
- {
- printf("AddCapacity::%s", strerror(errno));
- return;
- }
- pc->p = str;
- pc->capacity += ADD_NUM;
- printf("增容成功\n");
- }
- }
其中:
ADD函数比较简单,只需要在每次增加前对其内存进行检查,然后格式化输入信息即可,每次增加一个联系人的信息后,其所对应的count计数也需要进行+1。
- void add(Con* pc)
- {
- assert(pc);
- AddCapacity(pc);
- printf("请输入名字:>\n");
- scanf("%s", pc->p[pc->count].name);
- printf("请输入性别:>\n");
- scanf("%s", pc->p[pc->count].sex);
- printf("请输入年龄:>\n");
- scanf("%d", &(pc->p[pc->count].age));
- printf("请输入电话:>\n");
- scanf("%s", pc->p[pc->count].tel);
- printf("请输入地址:>\n");
- scanf("%s", pc->p[pc->count].address);
- printf("输入成功\n");
- pc->count++;
- }
以上三个函数需要用到查找函数,先假设所有人的名字不重复,根据人的姓名进行查找,返回其所属的count值。
- int FindByName(const Con* pc)
- {
- assert(pc);
- int i = 0;
- char input[20] = {0};
- printf("请输入名字\n");
- scanf("%s", input);
- for (i = 0; i
count; i++) - {
- if (strcmp(input, pc->p[i].name) == 0)
- {
- return i;
- }
- }
- return -1;
- }
其中:
当查找函数完成后,剩余的删查改函数就简单许多,需要注意的是:删除函数需要更改count,并且使所有人信息往前挪一位。
删除函数
- //删除
- void del(Con* pc)
- {
- assert(pc);
- if (pc->count == 0)
- {
- printf("联系人为空\n");
- return;
- }
- int i = FindByName(pc);
- if (i != -1)
- {
- int j = 0;
- for (j = i; j < pc->count; j++)
- {
- pc->p[j] = pc->p[j + 1];
- }
- pc->count--;
- printf("删除成功!\n");
- }
- else
- printf("没有找到!\n");
- }
查找并显示函数
- void check(const Con* pc)
- {
- assert(pc);
- int i = FindByName(pc);
- if (i != -1 )
- {
- printf("%-20s%-10s%-10s%-20s%-30s\n", "姓名", "性别", "年龄", "电话", "地址");
- printf("%-20s%-10s%-10d%-20s%-30s\n",
- pc->p[i].name,
- pc->p[i].sex,
- pc->p[i].age,
- pc->p[i].tel,
- pc->p[i].address);
- }
- else
- printf("没有找到!\n");
- }
修改函数
- void modify(Con* pc)
- {
- assert(pc);
- if (pc->count == 0)
- {
- printf("联系人为空\n");
- return;
- }
- int i = FindByName(pc);
- if (i != -1)
- {
- printf("请输入名字:>\n");
- scanf("%s", pc->p[i].name);
- printf("请输入性别:>\n");
- scanf("%s", pc->p[i].sex);
- printf("请输入年龄:>\n");
- scanf("%d", &(pc->p[i].age));
- printf("请输入电话:>\n");
- scanf("%s", pc->p[i].tel);
- printf("请输入地址:>\n");
- scanf("%s", pc->p[i].address);
- printf("修改成功!\n");
- }
- else
- printf("没有找到!\n");
- }
从第一行开始循环打印,以联系人数量count为限制,打印时为了保持美观,最好设计一个表头。
- void show(const Con* pc)
- {
- assert(pc);
- if (pc->count == 0)
- {
- printf("联系人为空\n");
- return;
- }
- printf("%-20s%-10s%-10s%-20s%-30s\n", "name", "sex", "age", "tel", "address");
- int i = 0;
- for (i = 0; i < pc->count; i++)
- {
- printf("%-20s%-10s%-10d%-20s%-30s\n",
- pc->p[i].name,
- pc->p[i].sex,
- pc->p[i].age,
- pc->p[i].tel,
- pc->p[i].address);
- }
- }
SORT排序函数
使用qsort函数进行排序。
- int cmp_by_name(const void* e1, const void* e2)
- {
- return strcmp(((Date*)e1)->name, ((Date*)e2)->name);
- }
-
- void sort(Con* pc)
- {
- assert(pc);
- qsort(pc->p, pc->count, sizeof(Date), cmp_by_name);
- printf("排序成功!\n");
- }
其中:
sqort函数使用方法如下:
DEL ALL一键删除
将通讯录重新初始化,就可以完成一键删除的功能了。
- void del_all(Con* pc)
- {
- assert(pc);
- InitCon(pc);
- printf("全部删除成功!\n");
- }
SaveCon保存通讯录
将内存中的数据以二进制的方式写入到txt文本中。
- void SaveCon(Con* pc)
- {
- assert(pc);
- FILE* pfwrite = fopen("con.txt", "w");
- if (pfwrite == NULL)
- {
- perror("SaveCon");
- return ;
- }
- int i = 0;
- for (i = 0; i < pc->count; i++)
- {
- fwrite(pc->p + i, sizeof(Date), 1, pfwrite);
- }
- printf("保存成功!\n");
- fclose(pfwrite);
- pfwrite = NULL;
- }
其中:
以二进制的读写打开txt文件后,会是一堆乱码,也可以格式化输出到txt文件中。代码如下:
- //格式化输出
- fprintf(pfwrite, "%10s%10s%10d%10s%10s\n",
- pc->p[i].name, pc->p[i].sex, pc->p[i].age, pc->p[i].tel, pc->p[i].address);
如果要使用格式化输出,在LoadCon加载函数中,信息载入的方式也需要进行修改。
Distory销毁函数
- void Distory(Con* pc)
- {
- assert(pc);
- free(pc->p);
- pc->p = NULL;
- }
直接释放内存,并将指针置空。
在写程序时,主要思想就是:先想好大致框架,每个函数的作用、参数以及返回值,框架构建好后依次完善函数即可。在写程序中,要养成步步调试,写注释的好习惯。