为什们要实现通讯录?
主要是为了让我们将结构体的知识,了解的更加深刻,将结构体应用一下,我们先将静态的通讯录实现,在进行改良,用动态内存的知识再将通讯录改造一边,将动态内容的知识也运用一下,最后再用文件操作的方式在改造一下通讯录。
所以我们会写三个通讯录,同时他是循序渐进的,一步一步的,如果不动前面的知识,可以去看看前面的知识点。
- 静态版本通讯录
- 动态版本通讯录
- 文件版本通讯录
🐖静态通讯录
主要运用结构体的知识,不会的同学可以看看《自定义类型保姆级教学(结构体,位段,枚举,联合)》
首先先介绍一下代码的主题框架有三个部分
test.c
,contact.c
,contact.h
。
test.c
的作用:测试通讯录的实现,也就是主函数,整个代码的操作逻辑
🐖contact.c
的作用:写通讯录的实现(通讯录代码函数实现)
🐖contact.h
的作用:写通讯录的声明(通讯录代码函数声明)
-
在引用头文件时需要注意的地方
-
🐖
contact.h
中放通讯录的声明,在test.c
和contact.c
的文件都要应用该声明,所以为什们我们不把#include<stdio.h>
这些头文件也放到声明里,我们就不需要在test.c
和contact.c
的文件重复定义了,那么怎么在在test.c
和contact.c
的文件中引用contact.h
文件呢,
🐕我们知道引用外部文件时,用双引号所以可以用include"contact.h"
,这样在contact.h
中存放的声明都可以使用。
对于通讯录需要存放100个人的信息,每个人的信息包括(姓名,性别,电话,年龄,住址)
而且通讯录还要有以下功能,
🐶 (1)增加联系人
🐶 (2)删除指定联系人
🐶 (3)修改指定联系人
🐶 (4)查找指定联系人
🐶 (5)排序
🐶 (6)显示通讯录的信息
🍔通讯录的代码实现
🦊(1)通讯录的外部封装
对于通讯录不可能看完做完一个操作就不做了,所以应该写一个循环,能够选择要进行的操作,等操作做完,想要退出循环也是可以的,我们又知道,这个操作至少要进行一次,所以我们学过的循环发现只有
do-while()
循环可以用了,但对于通讯录来说他有很多操作,所以要用switch,case
语句选择操作,但是又发现case
后面只能跟整型表达式;所以要用一个菜单函数将这些操作与整型数字联系起来,比如在菜单函数中规定1-就是增加联系人,当switch
选择1时就是增加联系人,
还需要注意一个点,也是在设计最巧妙的一个点,设置0
的时候退出程序,在case
后整型是0的时候break
,然后到while()
循环用0
,就可以跳出循环,因为0
为假。
在test.c 文件中写这个主函数
#include<stdio.h>
void menu()
{
printf("***********************************\n");
printf("****1-增加联系人 2-删除联系人****\n");
printf("****3-修改联系人 4-查找联系人****\n");
printf("****5-排序 6-显示通讯录****\n");
printf("**** 0-退出程序 ****\n");
printf("***********************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择操作->");
scanf("%d", &input);
switch(input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 0:
printf("退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
🦊(2)创建一个通讯录
-
<创建一个结构体>
-
🐖想要创建一个通讯录,就要创建一个结构体,用来表示一个人的信息(姓名,性别,电话,年龄,住址),通讯录中每个人的信息,而通讯录就想当与,众多人的集合,所以可以创建一个结构体数组。
🐕而对于这个表示一个人信息的结构体,因为也只是一个结构体,我们就放在通讯录声明的文件当中,也就是contact.h
文件。
//创建一个结构体,表示一个人的信息
struct PeoInf
{
char name[20];
char sex[20];
char tele[12];
int age;
char addr[30];
};
-
<创建一个通讯录>
-
🐖创建好这个记录每个人信息的结构体,我们现在就可以创建通讯录了,而了解本质我们就知道,通讯录就是这个结构体数组。放到代码就是
struct PeoInf data[100];
这样就创建了可以存放100个人信息的通讯录。
🐕但是大家觉得这样就完事了吗?那就大错特错了,这样想如果你想要增加一个信息,你要增加那个位置,你是不知道的,所以对于通讯录来说,我们不仅要创建这个数组,也要知道这个通讯录有几个信息,这时我们创建一个int sz;
来表示通讯录中有几个人的信息,所以将这两个内容再封装成一个结构体。代码如下,因为这是封装一个通讯录结构体,所以也放在contact.h
文件中。
有了通讯录的结构体,我们就可以在主函数中创建一个通讯录。
🦊(3)初始化通讯录
对于创建好了通讯录,对于通讯录的内容,应该先初始化,之后才可以进行修改。下面就要进行通讯录的初始化,注意他是传的是地址,不可以传值,如果传值,他是形参的一份临时拷贝,不会改变原本的参数,也就不能初始化。
void InitContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
pc->sz = 0;
memset(pc->data, 0, 100 * sizeof(struct PeoInf));
}
🦊(4)增加通讯录(功能一:增)
代码实现,在代码中用注释解释,看完代码相信你就能理解了
//增加通讯录
void AddContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
//判断通讯录的信息满了没有,就是看sz的大小
//if (pc->sz == 100)
//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
//这时我们可以把通讯录中人的信息也用define定义
if(pc -> sz == MAX)
{
printf("通讯录已满,无法增加数据\n");
return;
//因为通讯录已经放不下了,应该退出函数,对于这
//个函数的返回值是void,所以不需要返回任何东西
}
else
{
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入年龄:>");
scanf("%s", &(pc->data[pc->sz].age));
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功增加联系人\n");
}
}
🦊(5)查找通讯录(功能二:查)
跟增加通讯录一样,在注释中解释为什们要这么写
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%s %s %s %d %s\n", pc->data[i].name,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].age,
pc->data[i].addr);
}
}
上图,是我用上面的代码增加了三个人的信息然后查看,发现他们乱七八糟,每一个都没有一一对齐,连一个表头都没有,所以我们就要将这些代码对齐然后,在创建一个表头,请看下面代码。
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
int i = 0;
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].age,
pc->data[i].addr);
}
}
🦊(6)删除通讯录-(功能三:删)
跟增加通讯录一样,在注释中解释为什们要这么写
}
//查找函数,通过名字找到这个人
int Findname(const struct Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(pc->data[i].name, name))
{
return i;
}
}
return -1;
}
//删除通讯录
void DelContact(struct Contact* pc)
{
char name[NAME];
printf("请输入删除人的名字:>");
scanf("%s", name);
//查找一下指定的人是否存在
//就构造一个函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要删除的人不存在\n");
}
else
{
//删除
int j = 0;
for (j = ret; j < pc->sz - 1 ; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("成功删除联系人\n");
}
}
🦊(7)查找通讯录-(功能三:查)
跟增加通讯录一样,在注释中解释为什们要这么写
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
//跟上面删除操作一样,只不过将删除变为查找
char name[NAME];
printf("请输入查找联系人的名字:>");
scanf("%s", name);
//查找一下查找的人是否存在
//就会用上面的函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//查找联系人,找到就打印出来
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].tele,
pc->data[ret].age,
pc->data[ret].addr);
}
🦊(8)修改通讯录-(功能三:改)
修改通讯录,就是先找到要修改的信息位置,然后再像增加通讯录一样重新录入一遍信息。
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
printf("请输入修改人的信息:>");
char name[NAME];
scanf("%s", name);
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//修改
//就相当于重新录入信息
printf("请输入名字:>");
scanf("%s", pc->data[ret].name);
printf("请输入性别:>");
scanf("%s", pc->data[ret].sex);
printf("请输入电话:>");
scanf("%s", pc->data[ret].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[ret].age));
printf("请输入地址:>");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
}
}
🦊(9)排序通讯录
要排序通讯录,就要学会使用qsort(可以在这个地方查找信息),我也写过一遍文章介绍这么用去sort函数《用冒泡排序模拟qsort库函数》,可以参考一下
下面代码是按年龄排序的,只要你将排序函数改一下内容就行了
//比较
int CmpByAge(const void* e1, const void* e2)
{
return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
//用qsort排序
qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
🦊(10)组装函数
我们将每个函数都写完了,但是运行起来需要在主函数中将这些函数串联起来,因此可以看下面的主函数的代码。
int main()
{
//创建一个通讯录
struct Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{
menu();
printf("请选择操作->");
scanf("%d", &input);
switch(input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
SerchContact(&con);
break;
case 5:
SortContact(&con);
break;
case 6:
ShowContact(&con);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
🐕小总结
这样通讯录就写完了,看一看各各文件的代码吧
🦊test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("***********************************\n");
printf("****1-增加联系人 2-删除联系人****\n");
printf("****3-修改联系人 4-查找联系人****\n");
printf("****5-排序 6-显示通讯录****\n");
printf("**** 0-退出程序 ****\n");
printf("***********************************\n");
}
int main()
{
//创建一个通讯录
struct Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{
menu();
printf("请选择操作->");
scanf("%d", &input);
switch(input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
SerchContact(&con);
break;
case 5:
SortContact(&con);
break;
case 6:
ShowContact(&con);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
🦊contact.h
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME 20
#define SEX 20
#define TELE 12
#define ADDR 30
#include<assert.h>
#include<stdlib.h>
struct PeoInfo
{
char name[NAME];
char sex[SEX];
char tele[TELE];
int age;
char addr[ADDR];
};
//通讯录
struct Contact
{
struct PeoInfo data[MAX];
int sz ;
};
//初始化通讯录
void InitContact(struct Contact* pc);
//增加通讯录的信息
void AddContact(struct Contact* pc);
//显示通讯录的信息
void ShowContact(const struct Contact* pc);
//删除通讯录的信息
void DelContact(struct Contact *pc);
//查找指定联系人
void SerchContact(const struct Contact* pc);
//修改指定联系人
void ModifyContact(struct Contact* pc);
//排序通讯录-按照年龄
void SortContact(struct Constact* pc);
🦊 contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void InitContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
pc->sz = 0;
memset(pc->data, 0, 100 * sizeof(struct PeoInfo));
}
//增加通讯录
void AddContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
//判断通讯录的信息满了没有,就是看sz的大小
//if (pc->sz == 100)
//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
//这时我们可以把通讯录中人的信息也用define定义
if(pc -> sz == MAX)
{
printf("通讯录已满,无法增加数据\n");
return;
//因为通讯录已经放不下了,应该退出函数,对于这
//个函数的返回值是void,所以不需要返回任何东西
}
else
{
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功增加联系人\n");
}
}
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
int i = 0;
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].age,
pc->data[i].addr);
}
}
//查找函数,通过名字找到这个人
static int Findname(const struct Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(pc->data[i].name, name))
{
return i;
}
}
return -1;
}
//删除通讯录
void DelContact(struct Contact* pc)
{
char name[NAME];
printf("请输入删除人的名字:>");
scanf("%s", name);
//查找一下指定的人是否存在
//就构造一个函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要删除的人不存在\n");
}
else
{
//删除
int j = 0;
for (j = ret; j < pc->sz - 1 ; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("成功删除联系人\n");
}
}
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
//跟上面删除操作一样,只不过将删除变为查找
char name[NAME];
printf("请输入查找联系人的名字:>");
scanf("%s", name);
//查找一下查找的人是否存在
//就会用上面的函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//查找联系人,找到就打印出来
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].tele,
pc->data[ret].age,
pc->data[ret].addr);
}
}
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
printf("请输入修改人的信息:>");
char name[NAME];
scanf("%s", name);
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//修改
//就相当于重新录入信息
printf("请输入名字:>");
scanf("%s", pc->data[ret].name);
printf("请输入性别:>");
scanf("%s", pc->data[ret].sex);
printf("请输入电话:>");
scanf("%s", pc->data[ret].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[ret].age));
printf("请输入地址:>");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
}
}
//比较
int CmpByAge(const void* e1, const void* e2)
{
return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
//用qsort排序
qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
🐖动态通讯录
主要运用动态内存开辟的知识,不会的同学可以看看《动态内存管理(malloc free calloc realloc)》
-
(1)为什们要设计动态通讯录
-
起始就是在静态通讯录上改造,并且对于动态开辟空间的函数的运用。
静态通讯只能存放100个人,但是这样就不够灵活,为了让通讯录可以更加灵活,就设计成动态通讯录
(2)设计动态通讯录的思路
-
首先先做一个通讯录,用
malloc
存放3个人空间,存放满了后,在用realloc
增加两个人的空间,这样就更加灵活,并且不会浪费空间。
对于这个我们创建通讯录的代码就要改变,因为malloc
创建的空间用void*
指针来接收,所以将data[]
变为指针,并且在多增加一个参数,容量参数capacity
,当sz
和capacity
相同时,增加2个空间。所以
🍔动态通讯录代码实现
🦊(1)创建通讯录的代码如下:
🐖
//通讯录
struct Contact
{
struct PeoInfo *data ;
int sz;
int capacity;
};
🦊(2)初始化通讯录
初始化通讯录,用
malloc
申请3个结构体的空间
void InitContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
pc->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3);
if (pc == NULL)
{
peeor("InitContact");
return;
}
else
{
pc -> sz = 0;
//pc->capacity = 3;
//我们发现这个3,我们一直再用,可以直接用define定义Capacity = 3
//这时我们可以把通讯录中人的信息也用define定义
pc->capacity = CAPATICY;
}
}
🦊(3)增加通讯录
对于增加那用不用改呢,答案是用的,因为当空间不够的时候就需要增加空间。
int check_capacity(struct Contact* pc)
{
if (pc->sz == pc->capacity)
{
//当前容量满了,现在用realloc增加容量
struct PeoInfo* str = (struct PeoInfo*)relloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
if (str == NULL)
{
printf("空间申请失败\n");
return 0;
}
else
{
pc->data = str;
//将申请的空间传给pc->data
pc->capacity = pc->capacity + 2;
//将容量变大2.
printf("增容成功\n");
return 1;
}
}
return 1;
}
//增加通讯录
void AddContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
//判断通讯录的信息满了没有,就是看sz的大小
//if (pc->sz == 100)
//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
//这时我们可以把通讯录中人的信息也用define定义
//我们可以封装一个函数来判断他是否需要增容
int ret = check_capacity(pc);
if (ret == 1)
{
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功增加联系人\n");
}
else
{
return;
}
}
🦊(4)销毁通讯录
我们知道
calloc
和free
是同时出现的,申请了空间,就肯定要销毁空间,所以在退出函数就可以添加一个函数,销毁函数。
void DistoryContact(struct Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
🐕动态总结
动态开辟内存还是相对与来说比较简单的,希望大家先将动态内存的知识看懂,再将动态通讯录来练习学会。
🦊动态test.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("***********************************\n");
printf("****1-增加联系人 2-删除联系人****\n");
printf("****3-修改联系人 4-查找联系人****\n");
printf("****5-排序 6-显示通讯录****\n");
printf("**** 0-退出程序 ****\n");
printf("***********************************\n");
}
int main()
{
//创建一个通讯录
struct Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{
menu();
printf("请选择操作->");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
SerchContact(&con);
break;
case 5:
SortContact(&con);
break;
case 6:
ShowContact(&con);
break;
case 0:
DistoryContact(&con);
printf("退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
🦊动态contact.h
#pragma once
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME 20
#define SEX 20
#define TELE 12
#define ADDR 30
#include<assert.h>
#include<stdlib.h>
#define CAPATICY 3
struct PeoInfo
{
char name[NAME];
char sex[SEX];
char tele[TELE];
int age;
char addr[ADDR];
};
//通讯录
struct Contact
{
struct PeoInfo *data ;
int sz;
int capacity;
};
//初始化通讯录
void InitContact(struct Contact* pc);
//增加通讯录的信息
void AddContact(struct Contact* pc);
//显示通讯录的信息
void ShowContact(const struct Contact* pc);
//删除通讯录的信息
void DelContact(struct Contact* pc);
//查找指定联系人
void SerchContact(const struct Contact* pc);
//修改指定联系人
void ModifyContact(struct Contact* pc);
//排序通讯录-按照年龄
void SortContact(struct Contact* pc);
//销毁通讯录
void DistoryContact(struct Contact*pc);
🦊动态contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void InitContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
pc->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3);
if (pc == NULL)
{
perror("InitContact");
return;
}
else
{
pc -> sz = 0;
//pc->capacity = 3;
//我们发现这个3,我们一直再用,可以直接用define定义Capacity = 3
//这时我们可以把通讯录中人的信息也用define定义
pc->capacity = CAPATICY;
}
}
int check_capacity(struct Contact* pc)
{
if (pc->sz == pc->capacity)
{
//当前容量满了,现在用realloc增加容量
struct PeoInfo* str = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
if (str == NULL)
{
printf("空间申请失败\n");
return 0;
}
else
{
pc->data = str;
//将申请的空间传给pc->data
pc->capacity = pc->capacity + 2;
//将容量变大2.
printf("增容成功\n");
return 1;
}
}
return 1;
}
//增加通讯录
void AddContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
//判断通讯录的信息满了没有,就是看sz的大小
//if (pc->sz == 100)
//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
//这时我们可以把通讯录中人的信息也用define定义
//我们可以封装一个函数来判断他是否需要增容
int ret = check_capacity(pc);
if (ret == 1)
{
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功增加联系人\n");
}
else
{
return;
}
}
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
int i = 0;
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].age,
pc->data[i].addr);
}
}
//查找函数,通过名字找到这个人
static int Findname(const struct Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(pc->data[i].name, name))
{
return i;
}
}
return -1;
}
//删除通讯录
void DelContact(struct Contact* pc)
{
char name[NAME];
printf("请输入删除人的名字:>");
scanf("%s", name);
//查找一下指定的人是否存在
//就构造一个函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要删除的人不存在\n");
}
else
{
//删除
int j = 0;
for (j = ret; j < pc->sz - 1; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("成功删除联系人\n");
}
}
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
//跟上面删除操作一样,只不过将删除变为查找
char name[NAME];
printf("请输入查找联系人的名字:>");
scanf("%s", name);
//查找一下查找的人是否存在
//就会用上面的函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//查找联系人,找到就打印出来
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].tele,
pc->data[ret].age,
pc->data[ret].addr);
}
}
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
printf("请输入修改人的信息:>");
char name[NAME];
scanf("%s", name);
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//修改
//就相当于重新录入信息
printf("请输入名字:>");
scanf("%s", pc->data[ret].name);
printf("请输入性别:>");
scanf("%s", pc->data[ret].sex);
printf("请输入电话:>");
scanf("%s", pc->data[ret].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[ret].age));
printf("请输入地址:>");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
}
}
//比较
int CmpByAge(const void* e1, const void* e2)
{
return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
//用qsort排序
qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
void DistoryContact(struct Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
🐖文件通讯录
主要运用文件操作的知识,不会的同学可以看看《C的文件操作》
-
文件通讯录的需求
- 通讯录退出后,之前保存的信息不能改变,当下一次重新运行通讯录的时候,还能看到上次保存的信息 分析需求
- 退出的时候,把数据保存到文件中,当下一次运行的时候,再从文件中加载信息就可以了
🍔文件通讯录代码实现
🦊保存通讯录
首先我们要实现这个需求,在退出之前,保存通讯录。
//保存通讯录
void SaveContact(struct Contact* pc)
{
//打开文件,用二进制的方式写
FILE* tt = fopen("Contact.txt", "wb");
//判断是否为空指针
if (tt == NULL)
{
perror("SaveContact:fopen");
return;
}
//可以写了
for (int i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i, sizeof(struct PeoInfo), 1, tt);
}
//关闭文件
fclose(tt);
tt = NULL;
return 0;
}
🦊加载通讯录
⭐将通讯录保存了,但是在打开通讯录的时候,需要能够读取文件的信息,但是这个操作在哪进行呀,我们可以在初始化通讯录的时候进行读取文件的信息操作。
static int check_capacity(struct Contact* pc);
void loadContact(struct Contact* pc)
{
//打开文件
FILE* tt = fopen("Contact.txt", "rb");
if (tt == NULL)
{
perror("loadContact:fopen");
return;
}
//读文件
//创建一个临时结构体,当中介
struct PeoInfo tmp = { 0 };
//因为这是动态版本,可能涉及到增容问题
//用他的返回值看,读取失败返回0;
while (fread(&tmp, sizeof(struct PeoInfo), 1, tt))
{
//考虑增容问题
check_capacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
//关闭文件
}
🦊文件test.c
文件的代码其实大致无差,就是多了这个功能,为了以防万一还是放在这里了。欢迎大家食用。
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
printf("***********************************\n");
printf("****1-增加联系人 2-删除联系人****\n");
printf("****3-修改联系人 4-查找联系人****\n");
printf("****5-排序 6-显示通讯录****\n");
printf("**** 0-退出程序 ****\n");
printf("***********************************\n");
}
int main()
{
//创建一个通讯录
struct Contact con;
//初始化通讯录
InitContact(&con);
int input = 0;
do
{
menu();
printf("请选择操作->");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
SerchContact(&con);
break;
case 5:
SortContact(&con);
break;
case 6:
ShowContact(&con);
break;
case 0:
SaveContact(&con);
DistoryContact(&con);
printf("退出程序\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
🦊文件contact.h
#pragma once
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME 20
#define SEX 20
#define TELE 12
#define ADDR 30
#include<assert.h>
#include<stdlib.h>
#define CAPATICY 3
struct PeoInfo
{
char name[NAME];
char sex[SEX];
char tele[TELE];
int age;
char addr[ADDR];
};
//通讯录
struct Contact
{
struct PeoInfo *data ;
int sz;
int capacity;
};
//初始化通讯录
void InitContact(struct Contact* pc);
//增加通讯录的信息
void AddContact(struct Contact* pc);
//显示通讯录的信息
void ShowContact(const struct Contact* pc);
//删除通讯录的信息
void DelContact(struct Contact* pc);
//查找指定联系人
void SerchContact(const struct Contact* pc);
//修改指定联系人
void ModifyContact(struct Contact* pc);
//排序通讯录-按照年龄
void SortContact(struct Contact* pc);
//销毁通讯录
void DistoryContact(struct Contact*pc);
//保存通讯录
void SaveContact(struct Contact* pc);
🦊动态contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
static int check_capacity(struct Contact* pc);
void loadContact(struct Contact* pc)
{
//打开文件
FILE* tt = fopen("Contact.txt", "rb");
if (tt == NULL)
{
perror("loadContact:fopen");
return;
}
//读文件
//创建一个临时结构体,当中介
struct PeoInfo tmp = { 0 };
//因为这是动态版本,可能涉及到增容问题
//用他的返回值看,读取失败返回0;
while (fread(&tmp, sizeof(struct PeoInfo), 1, tt))
{
//考虑增容问题
check_capacity(pc);
pc->data[pc->sz] = tmp;
pc->sz++;
}
//关闭文件
}
void InitContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
pc->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3);
if (pc == NULL)
{
perror("InitContact");
return;
}
else
{
pc -> sz = 0;
//pc->capacity = 3;
//我们发现这个3,我们一直再用,可以直接用define定义Capacity = 3
//这时我们可以把通讯录中人的信息也用define定义
pc->capacity = CAPATICY;
}
//加载通讯录,让文件的信息加载到通讯录
loadContact(pc);
}
static int check_capacity(struct Contact* pc)
{
if (pc->sz == pc->capacity)
{
//当前容量满了,现在用realloc增加容量
struct PeoInfo* str = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
if (str == NULL)
{
printf("空间申请失败\n");
return 0;
}
else
{
pc->data = str;
//将申请的空间传给pc->data
pc->capacity = pc->capacity + 2;
//将容量变大2.
printf("增容成功\n");
return 1;
}
}
return 1;
}
//增加通讯录
void AddContact(struct Contact* pc)
{
//防止是空指针,用断言函数
assert(pc);
//判断通讯录的信息满了没有,就是看sz的大小
//if (pc->sz == 100)
//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
//这时我们可以把通讯录中人的信息也用define定义
//我们可以封装一个函数来判断他是否需要增容
int ret = check_capacity(pc);
if (ret == 1)
{
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->data[pc->sz].sex);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[pc->sz].age));
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("成功增加联系人\n");
}
else
{
return;
}
}
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
int i = 0;
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].age,
pc->data[i].addr);
}
}
//查找函数,通过名字找到这个人
static int Findname(const struct Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(pc->data[i].name, name))
{
return i;
}
}
return -1;
}
//删除通讯录
void DelContact(struct Contact* pc)
{
char name[NAME];
printf("请输入删除人的名字:>");
scanf("%s", name);
//查找一下指定的人是否存在
//就构造一个函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要删除的人不存在\n");
}
else
{
//删除
int j = 0;
for (j = ret; j < pc->sz - 1; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("成功删除联系人\n");
}
}
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
//跟上面删除操作一样,只不过将删除变为查找
char name[NAME];
printf("请输入查找联系人的名字:>");
scanf("%s", name);
//查找一下查找的人是否存在
//就会用上面的函数
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//查找联系人,找到就打印出来
printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
pc->data[ret].sex,
pc->data[ret].tele,
pc->data[ret].age,
pc->data[ret].addr);
}
}
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
printf("请输入修改人的信息:>");
char name[NAME];
scanf("%s", name);
int ret = Findname(pc, name);
if (ret == -1)
{
printf("要查找的人不存在\n");
}
else
{
//修改
//就相当于重新录入信息
printf("请输入名字:>");
scanf("%s", pc->data[ret].name);
printf("请输入性别:>");
scanf("%s", pc->data[ret].sex);
printf("请输入电话:>");
scanf("%s", pc->data[ret].tele);
printf("请输入年龄:>");
scanf("%d", &(pc->data[ret].age));
printf("请输入地址:>");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
}
}
//比较
int CmpByAge(const void* e1, const void* e2)
{
return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
//用qsort排序
qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
void DistoryContact(struct Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->capacity = 0;
pc->sz = 0;
}
//保存通讯录
void SaveContact(struct Contact* pc)
{
//打开文件,用二进制的方式写
FILE* tt = fopen("Contact.txt", "wb");
//判断是否为空指针
if (tt == NULL)
{
perror("SaveContact:fopen");
return;
}
//可以写了
for (int i = 0; i < pc->sz; i++)
{
fwrite(pc->data + i, sizeof(struct PeoInfo), 1, tt);
}
//关闭文件
fclose(tt);
tt = NULL;
return 0;
}
🐖总结
⭐我们现在就将三种形式的通讯录写完了,相信大家肯定或多多少都有点收获,如果大家不太理解,可以私信我,看到必会,如果对大家有点帮助,希望大家一键三联,这将是我的动力。欢迎大家食用。😊😊