通讯录的实现(优化版:动态增长,文件保存)

简介: 本文将会用c语言实现一个通讯录的系统,并且存储若干人的信息,每个人的信息包括:姓名,性别,年龄,电话号码,住址。此通讯录系统的功能包括: 1.增加联系人 2.删除对应的联系人 3.查找联系人 4.修改联系人的信息 5.排序此通讯录 6.打印出通讯录每个人的信息为了实现这些变量,并且方便后期的处理数组大小,所以我们可以利用宏来实现这个功能。


一、前言
本文将会用c语言实现一个通讯录的系统,并且存储若干人的信息,每个人的信息包括:姓名,性别,年龄,电话号码,住址。此通讯录系统的功能包括: 1.增加联系人 2.删除对应的联系人 3.查找联系人 4.修改联系人的信息 5.排序此通讯录 6.打印出通讯录每个人的信息

二、通讯录的实现
1.关于通讯录的前期准备
(1)关于全局变量的定义
为了实现这些变量,并且方便后期的处理数组大小,所以我们可以利用宏来实现这个功能

define MAX 1000

define MAX_NAME 20

define MAX_SEX 5

define MAX_TELE 12

define MAX_ADDR 30

(2)菜单的实现
首先关于一个通讯录,建立一个菜单是很重要的,菜单能够实现和用户的交互。

因此我们需要建立一个菜单,并且菜单立马应该包括通讯录立马该有的功能,以便于用户的操作

代码如下:

void menu()
{
printf("**\n");
printf("* 1.add 2.del *\n");
printf("* 3.search 4.modify *\n");
printf("* 5.show 6sort *\n");
printf("* 0.exit *\n");
printf("**\n");
}

效果大致这样就可以;

(3)关于联系人结构体的创建
这里我们需要利用结构体来实现实现前言中的通讯录功能以及联系人信息,我将利用两个结构体来构建我们需要的东西。(大小设置为1000)(最多存放1000个人的信息)

struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};

struct contact
{
struct PeoInfo date[MAX];
int size;
};

(4)实现菜单选项的功能
我们需要根据菜单里面的选项来选择进行我们需要实现的功能,比如我们想假如一个用户信息,我们就输入1就会进行用户假如的操作,我们想退出程序我们输入0就可以退出。我选择利用枚举变量的形式来实现,讲操作变成数字,利用switch选择语句来实现各自的功能。

使用枚举的好处就是方便了我们补充函数内容的时候方便得知那个数字对应那个功能

enum option
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SHOW, //5
SORT,//6
};

然后使用switch的时候便会方便许多,如下

int input = 0;
//创建结构体数组
struct contact con;
//初始化结构体数组
Initcon(&con);

do {
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
Addcontact(&con);
break;
case DEL:
Delcontact(&con);
break;
case SEARCH:
Searchcontact(&con);
break;
case MODIFY:
Modifycontact(&con);
break;
case SHOW:
Showcontact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}

} while (input);

我们case的后面不在是1 2 3 而是对应功能的名字,是不是很方便呢

2、通讯录的功能实现
(1)初始化通讯录
第一步初始化的话,我们要知道要将通讯录初始化什么内容,初始化的目的就是为了避免因不进行初始化导致存的是随机值,从而造成不必要的麻烦,一般的初始化就是放0或者为空;这里我们可以浅浅用一个memset函数来实现,当然别忘了引用string的头文件,然后sz的初始化很简单就是初始化为0。

memset(ps->date, 0, sizeof(ps->date));
ps->size = 0;

代码就是这么简单,但是我们为了书写的时候便于看,一般分多个项目进行写一个小游戏;

这里只展示函数的实现部分(下同)

void Initcon(struct contact* ps)
{
assert(ps!=NULL);
memset(ps->date, 0, sizeof(ps->date));
ps->size = 0;
}

(2)增加联系人
首先第一步我们完成第一步就可以创建关于加入联系人的函数,这个很简单我们只需要访问通讯录结构体里面的数组中的每个元素然后输入对应值就可以了

void Addcontact(struct contact* ps)
{
assert(ps != NULL);
if (ps->size == MAX)
{
printf("通讯录已满,无法再进行添加\n");
}
else
{
printf("情输入姓名:>");
scanf("%s", ps->date[ps->size].name);
printf("情输入年龄:>");
scanf("%d", &(ps->date[ps->size].age));
printf("情输入性别:>");
scanf("%s", ps->date[ps->size].sex);
printf("情输入电话:>");
scanf("%s", ps->date[ps->size].tele);
printf("情输入地址:>");
scanf("%s", ps->date[ps->size].addr);

    ps->size++;//千万要记得++

    printf("录入信息完成\n");
}

}

(3)删除联系人
这个操作也不算复杂,我的思路是,首先我们先利用刚刚查找的嘞个查找下标的函数,查找到我们需要寻找删除联系人的坐标,然后对他进行删除,删除之后呢我们需要把后面的元素挨个往前移动,这就要利用for循环,但是对于for循环的次数要多加注意,因为稍不小心就会导致数组越界。

static int Find_by_name(const struct contact ps, char name[MAX_NAME])
{//static修饰可以使其函数仅可以再本。c生效
//避免了污染
assert(ps != NULL);
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (strcmp(ps->date[i].name, name) == 0)
return i;
}
return -1;
}
void Delcontact(struct contact
ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
//查找
//找到返回pos下标
//找不到返回-1
int pos=Find_by_name(ps, name);
if (pos == -1)
{
printf("要删除的联系人不存在\n");
}
else
{
int j = 0;
for (j = pos; j size ; j++)//
//*
{
ps->date[j] = ps->date[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}

这个代码其中有一个地方加了 static 这一部分就很妙,就好比如如果别的。c文件再写的时候跟这个函数有关系,就可以避免发生关系,就会使得 Find_by_name这个函数只会在本文件下生效,避免了污染,就好比生物上的物种感染,你可以不用,但不能没有,避免发生麻烦;

(4)打印通讯录
打印通讯录也很简单,利用一个for循环根据sz的大小遍历结构体中的数组每个元素并且打印即可

代码如下:

void Showcontact(const struct contact* ps)
{
assert(ps != NULL);
int i = 0;
if (ps->size == 0)
{
printf("通讯录内容为空,无法打印\n");
}
else
{
for (i = 0; i < ps->size; i++)
{
printf("%-20s\t%-3s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");//为了打印出来的内容对其,显得好看对其的打印要一直
printf("%-20s\t%-3d\t%-5s\t%-12s\t%-20s\n",//就先这跟上面的一样才好看
ps->date[i].name,
ps->date[i].age,
ps->date[i].sex,
ps->date[i].tele,
ps->date[i].addr);
}
}
}

(5)查找联系人
查找联系人这边我们需要构建一个函数,这个函数需要去根据我们想要寻找的姓名去在通讯录中寻找这个人所对应的位置,加入找到了就可以返回对应位置的下标,否则返回-1。找到之后就和打印通讯录的操作差不多打印出来就好了。然而查找可以通过多种方式,年龄啊,电话啊,这里使用的是姓名

void Searchcontact(const struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要查找联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要查找的联系人不存在\n");
}
else
{
printf("信息如下:\n");
printf("%-20s\t%-3s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-3d\t%-5s\t%-12s\t%-20s\n",
ps->date[pos].name,
ps->date[pos].age,
ps->date[pos].sex,
ps->date[pos].tele,
ps->date[pos].addr);
}
}

(6)修改联系人

所有,我们可以先让用户输入想要修改人的信息,然后再重新在add所有信息

void Modifycontact(struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要修改联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
}
else
{
printf("情输入姓名:>");
scanf("%s", ps->date[pos].name);
printf("情输入年龄:>");
scanf("%d", &(ps->date[pos].age));
printf("情输入性别:>");
scanf("%s", ps->date[pos].sex);
printf("情输入电话:>");
scanf("%s", ps->date[pos].tele);
printf("情输入地址:>");
scanf("%s", ps->date[pos].addr);
printf("修改信息完成\n");
}
}

我们不妨先查找到需要修改人,然后先让用户查找到需要修改的这个人然后选择是修改什么信息,然后重新输入改后的信息,这个其实就和菜单选项实现很相似,我们需要利用枚举变量,跟do。。。while

enum Information
{
ERRO,
NAME,
AGE,
SEX,
TELE,
ADDR,
};

void Modifycontact(struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要修改联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
}
else
{
int input = 0;
do
{
printf("请输入你想修改此用户的哪项信息\n");
printf(" 0.退出 1.姓名 2.年龄 3.性别 4.电话 5.地址:>\n");
scanf("%d", &input);
switch (input)
{
case NAME:
printf("情输入姓名:>");
scanf("%s", ps->date[pos].name);
break;
case AGE:
printf("情输入年龄:>");
scanf("%d", &(ps->date[pos].age));
break;
case SEX:
printf("情输入性别:>");
scanf("%s", ps->date[pos].sex);
break;
case TELE:
printf("情输入电话:>");
scanf("%s", ps->date[pos].tele);
break;
case ADDR:
printf("情输入地址:>");
scanf("%s", ps->date[pos].addr);
break;
case ERRO:
printf("修改完成\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}
} while (input);
}
}

值得注意的是

这里的第一个没有写EXIT,而是ERRO是因为我们第一次使用枚举的时候已经定义过EXIT了再次定义会有错误,重定义

效果如下

(7)排序通讯录
这一步如果单靠我们自己实现是很麻烦的,但是c语言的库函数里面右qsort,使用该库函数实现起来就非常的简单

int con_name(const void e1, const void e2)
{
return (strcmp(((struct contact)e1)->date->name, ((struct contact)e2)->date->name));
}//这里一定要记得强制类型转换
void SortContact(struct contact* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("通讯录为空,无法排序\n");
return;
}
qsort(ps->date, ps->size, sizeof(ps->date[0]), con_name);
printf("排序成功\n");
Showcontact(ps);
}

重要的内容说三遍

总结:
test.c

define _CRT_SECURE_NO_WARNINGS

include"contact.h"

void menu()
{
printf("**\n");
printf("* 1.add 2.del *\n");
printf("* 3.search 4.modify *\n");
printf("* 5.show 6sort *\n");
printf("* 0.exit *\n");
printf("**\n");
}
int main()
{
int input = 0;
//创建结构体数组
struct contact con;
//初始化结构体数组
Initcon(&con);
do {
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
Addcontact(&con);
break;
case DEL:
Delcontact(&con);
break;
case SEARCH:
Searchcontact(&con);
break;
case MODIFY:
Modifycontact(&con);
break;
case SHOW:
Showcontact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}

} while (input);
return 0;

}

contact.c

define _CRT_SECURE_NO_WARNINGS

include"contact.h"

void Initcon(struct contact* ps)
{
assert(ps!=NULL);
memset(ps->date, 0, sizeof(ps->date));
ps->size = 0;
}

void Addcontact(struct contact* ps)
{
assert(ps != NULL);
if (ps->size == MAX)
{
printf("通讯录已满,无法再进行添加\n");
}
else
{
printf("情输入姓名:>");
scanf("%s", ps->date[ps->size].name);
printf("情输入年龄:>");
scanf("%d", &(ps->date[ps->size].age));
printf("情输入性别:>");
scanf("%s", ps->date[ps->size].sex);
printf("情输入电话:>");
scanf("%s", ps->date[ps->size].tele);
printf("情输入地址:>");
scanf("%s", ps->date[ps->size].addr);
ps->size++;
printf("录入信息完成\n");
}
}

void Showcontact(const struct contact* ps)
{
assert(ps != NULL);
int i = 0;
if (ps->size == 0)
{
printf("通讯录内容为空,无法打印\n");
}
else
{
for (i = 0; i < ps->size; i++)
{
printf("%-20s\t%-3s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-3d\t%-5s\t%-12s\t%-20s\n",
ps->date[i].name,
ps->date[i].age,
ps->date[i].sex,
ps->date[i].tele,
ps->date[i].addr);
}
}
}

static int Find_by_name(const struct contact ps, char name[MAX_NAME])
{//static修饰可以使其函数仅可以再本。c生效
//避免了污染
assert(ps != NULL);
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (strcmp(ps->date[i].name, name) == 0)
return i;
}
return -1;
}
void Delcontact(struct contact
ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
//查找
//找到返回pos下标
//找不到返回-1
int pos=Find_by_name(ps, name);
if (pos == -1)
{
printf("要删除的联系人不存在\n");
}
else
{
int j = 0;
for (j = pos; j size ; j++)//
//*
{
ps->date[j] = ps->date[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}

void Searchcontact(const struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要查找联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要查找的联系人不存在\n");
}
else
{
printf("信息如下:\n");
printf("%-20s\t%-3s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-3d\t%-5s\t%-12s\t%-20s\n",
ps->date[pos].name,
ps->date[pos].age,
ps->date[pos].sex,
ps->date[pos].tele,
ps->date[pos].addr);
}
}

//void Modifycontact(struct contact* ps)
//{
// assert(ps != NULL);
// char name[MAX_NAME];
// printf("请输入要修改联系人的姓名:>");
// scanf("%s", name);
// int pos = Find_by_name(ps, name);
// if (pos == -1)
// {
// printf("要修改的联系人不存在\n");
// }
// else
// {
// printf("情输入姓名:>");
// scanf("%s", ps->date[pos].name);
// printf("情输入年龄:>");
// scanf("%d", &(ps->date[pos].age));
// printf("情输入性别:>");
// scanf("%s", ps->date[pos].sex);
// printf("情输入电话:>");
// scanf("%s", ps->date[pos].tele);
// printf("情输入地址:>");
// scanf("%s", ps->date[pos].addr);
// printf("修改信息完成\n");
// }
//}

void Modifycontact(struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要修改联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
}
else
{
int input = 0;
do
{
printf("请输入你想修改此用户的哪项信息\n");
printf(" 0.退出 1.姓名 2.年龄 3.性别 4.电话 5.地址:>\n");
scanf("%d", &input);
switch (input)
{
case NAME:
printf("情输入姓名:>");
scanf("%s", ps->date[pos].name);
break;
case AGE:
printf("情输入年龄:>");
scanf("%d", &(ps->date[pos].age));
break;
case SEX:
printf("情输入性别:>");
scanf("%s", ps->date[pos].sex);
break;
case TELE:
printf("情输入电话:>");
scanf("%s", ps->date[pos].tele);
break;
case ADDR:
printf("情输入地址:>");
scanf("%s", ps->date[pos].addr);
break;
case ERRO:
printf("修改完成\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}
} while (input);
}
}

int con_name(const void e1, const void e2)
{
return (strcmp(((struct contact)e1)->date->name, ((struct contact)e2)->date->name));
}
void SortContact(struct contact* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("通讯录为空,无法排序\n");
return;
}
qsort(ps->date, ps->size, sizeof(ps->date[0]), con_name);
printf("排序成功\n");
Showcontact(ps);
}

contact.h

define _CRT_SECURE_NO_WARNINGS

include

include

include

include

define MAX 1000

define MAX_NAME 20

define MAX_SEX 5

define MAX_TELE 12

define MAX_ADDR 30

enum option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
};
enum Information
{
ERRO,
NAME,
AGE,
SEX,
TELE,
ADDR,
};

struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};

struct contact
{
struct PeoInfo date[MAX];
int size;
};

void Initcon(struct contact* ps);

void Addcontact(struct contact* ps);

void Showcontact(const struct contact* ps);

void Delcontact(struct contact* ps);

void Searchcontact(const struct contact* ps);

void Modifycontact(struct contact* ps);

优化部分:
这个通讯录我们不难发现他有一个致命的缺点,就是我们初始化的通讯录大小为1000,但是当我们想要存的内容不足1000的时候,直接开辟1000个元素空间,未免有点浪费空间,又或者说我们存的元素超过1000,那么编译器就会给我们报错,我们就需要继续手动增加空间,所以为了避免这样我们可以利用动态内存分配来定义我们的通讯录结构体。

我们需要多少就开辟多少空间,那么就很智能。

1、通讯录结构体的改进
这边我们可以把之前的结构体里面的数组变成一个指针数组,并且为了考虑这个通讯录来回删除增加的缘故,一个sz来记录数组元素个数是不行的,我们需要在设定一个值为数组最大的空间,当sz和他相等的时候我们就需要扩充这个数组。我们先设定原来容量为3,当达到原本的容量以后,在进行开辟两个空间。

struct contact
{
struct PeoInfo date[MAX];
int size;
};

struct contact
{
struct PeoInfo *date;
int size;
int capacity;
};

2、初始化结构体的改进
我们思考后大概知道需要改进的内容分别是初始化与增加

这个初始化结构体,我们需要sz初始化为0,并且为指针开辟一块空间,并且赋予capacity一个初始值,我们一开始说过了一开始默认容量为3,那么需要用宏的定义去定义capacity。

memset(ps->date, 0, sizeof(ps->date));
ps->size = 0;

define CAPACITY 3

void Initcon(struct contact ps)
{
assert(ps != NULL);
ps->size = 0;
ps->capacity = CAPACITY;
ps->date = (struct PeoInfo
)malloc(ps->capacity * sizeof(struct PeoInfo));
if (ps->date == NULL)
{
printf("%s\n", strerror(errno));
return;
}
}

3、add部分的改进
add部分改的思路就是,原本的空间不再是1000而是默认的3,所以我们在add的时候需要进行判断是否达到capacity,达不到就进行正常add,达到了就进行增容处理;

void Addcontact(struct contact* ps)
{
assert(ps != NULL);
if (ps->size == MAX)
{
printf("通讯录已满,无法再进行添加\n");
}
else
{
printf("情输入姓名:>");
scanf("%s", ps->date[ps->size].name);
printf("情输入年龄:>");
scanf("%d", &(ps->date[ps->size].age));
printf("情输入性别:>");
scanf("%s", ps->date[ps->size].sex);
printf("情输入电话:>");
scanf("%s", ps->date[ps->size].tele);
printf("情输入地址:>");
scanf("%s", ps->date[ps->size].addr);

    ps->size++;//千万要记得++

    printf("录入信息完成\n");
}

}

void Checkcapacity(struct contact ps)
{
if (ps->size == ps->capacity)
{
struct PeoInfo
str=realloc(ps->date, (ps->capacity + 2) * sizeof(struct PeoInfo));
if (str != NULL)
{
ps->date = str;
ps->capacity += 2;
printf("增容成功\n");
}
else
{
printf("%s\n", strerror(errno));
printf("增容失败\n");
}
}
}

void Addcontact(struct contact* ps)
{
assert(ps != NULL);
Checkcapacity(ps);
printf("情输入姓名:>");
scanf("%s", ps->date[ps->size].name);
printf("情输入年龄:>");
scanf("%d", &(ps->date[ps->size].age));
printf("情输入性别:>");
scanf("%s", ps->date[ps->size].sex);
printf("情输入电话:>");
scanf("%s", ps->date[ps->size].tele);
printf("情输入地址:>");
scanf("%s", ps->date[ps->size].addr);
ps->size++;
printf("录入信息完成\n");
}

4、释放内存
在我们使用完该通讯录以后,因为我们优化后使用了动态内存,所以要进行正规的操作,也就是内存释放

void DestroyContact(struct contact* ps)
{
assert(ps);
free(ps->date);
ps->date = NULL;
ps->size = 0;
printf("销毁成功\n");
}

放的地方也就是在

5、文件保存
我们没使用过通讯录的就会像上面所说的步骤一下,初始化,添加,删除,什么的,但是我使用过一次后,再后续使用的时候,是不是应该保存以前使用过的所有信息呢?那么一下操作就是实现该功能,主要知识是涉及到文件操作部分

我们还需要添加一个新的功能选项,也就是保存数据:savecontact

enum option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
SAVE,
};
//菜单:
void menu()
{
printf("**\n");
printf("* 1.add 2.del *\n");
printf("* 3.search 4.modify *\n");
printf("* 5.show 6sort *\n");
printf("* 7.save 0.exit *\n");
printf("**\n");
}

int main()
{
int input = 0;
//创建结构体数组
struct contact con;
//初始化结构体数组
Initcon(&con);
do {
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
switch (input)
{
//...
case SAVE:
SaveContact(&con);
break;
//...
}

} while (input);
return 0;

}

接下来就是实现savecontact

void SaveContact(struct contact ps)
{
FILE
pfwrite = fopen("contact.dat", "wb");
if (pfwrite == NULL)
{
printf("SaveContact::%s", strerror(errno));
return;
}
int i = 0;
for (i = 0; i < ps->size; i++)
{
fwrite(&(ps->date[i]), sizeof(struct PeoInfo), 1, pfwrite);
}
fclose(pfwrite);
pfwrite = NULL;
}

那我们做了以上的操作过后,感觉可以了,但细心思考过后发现,我们还需要进行改代码把初始化部分进行修改,要不然的话每次都会将通讯录初始化为0,这样显然是不可以,改进后的初始化我们想达到的目就是:初始化结果为上次已经输入过的数据;

void Checkcapacity(struct contact* ps);

void Initcon(struct contact ps)
{
assert(ps != NULL);
ps->size = 0;
ps->capacity = CAPACITY;
ps->date = (struct PeoInfo
)malloc(ps->capacity * sizeof(struct PeoInfo));
if (ps->date == NULL)
{
printf("%s\n", strerror(errno));
return;
}
//将文件的信息存贮到通讯录里面
loadcontact(ps);
}

void loadcontact(struct contact ps)
{
struct PeoInfo tmp = { 0 };
FILE
pfread = fopen("contact.dat", "rb");
if (pfread == NULL)
{
printf("loadcontact::%s\n", strerror(errno));
return;
}
while (fread(&(tmp), sizeof(struct PeoInfo), 1, pfread))
{
Checkcapacity(ps);
ps->date[ps->size] = tmp;
ps->size++;
}
fclose(pfread);
pfread = NULL;
}

void Checkcapacity(struct contact ps)
{
if (ps->size == ps->capacity)
{
struct PeoInfo
str=realloc(ps->date, (ps->capacity + 2) * sizeof(struct PeoInfo));
if (str != NULL)
{
ps->date = str;
ps->capacity += 2;
printf("增容成功\n");
}
else
{
printf("%s\n", strerror(errno));
printf("增容失败\n");
}
}
}

总结:
test.c(优化版)

define _CRT_SECURE_NO_WARNINGS

include"contact.h"

void menu()
{
printf("**\n");
printf("* 1.add 2.del *\n");
printf("* 3.search 4.modify *\n");
printf("* 5.show 6sort *\n");
printf("* 7.save 0.exit *\n");
printf("**\n");
}
int main()
{
int input = 0;
//创建结构体数组
struct contact con;
//初始化结构体数组
Initcon(&con);
do {
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
Addcontact(&con);
break;
case DEL:
Delcontact(&con);
break;
case SEARCH:
Searchcontact(&con);
break;
case MODIFY:
Modifycontact(&con);
break;
case SHOW:
Showcontact(&con);
break;
case SORT:
SortContact(&con);
break;
case SAVE:
SaveContact(&con);
break;
case EXIT:
SaveContact(&con);
DestroyContact(&con);
printf("退出通讯录\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}

} while (input);
return 0;

}

contact.c(优化版)

define _CRT_SECURE_NO_WARNINGS

include"contact.h"

void Checkcapacity(struct contact* ps);

void Initcon(struct contact ps)
{
assert(ps != NULL);
ps->size = 0;
ps->capacity = CAPACITY;
ps->date = (struct PeoInfo
)malloc(ps->capacity * sizeof(struct PeoInfo));
if (ps->date == NULL)
{
printf("%s\n", strerror(errno));
return;
}
//将文件的信息存贮到通讯录里面
loadcontact(ps);
}

void loadcontact(struct contact ps)
{
struct PeoInfo tmp = { 0 };
FILE
pfread = fopen("contact.dat", "rb");
if (pfread == NULL)
{
printf("loadcontact::%s\n", strerror(errno));
return;
}
while (fread(&(tmp), sizeof(struct PeoInfo), 1, pfread))
{
Checkcapacity(ps);
ps->date[ps->size] = tmp;
ps->size++;
}
fclose(pfread);
pfread = NULL;
}

void Checkcapacity(struct contact ps)
{
if (ps->size == ps->capacity)
{
struct PeoInfo
str=realloc(ps->date, (ps->capacity + 2) * sizeof(struct PeoInfo));
if (str != NULL)
{
ps->date = str;
ps->capacity += 2;
printf("增容成功\n");
}
else
{
printf("%s\n", strerror(errno));
printf("增容失败\n");
}
}
}

void Addcontact(struct contact* ps)
{
assert(ps != NULL);
Checkcapacity(ps);
printf("情输入姓名:>");
scanf("%s", ps->date[ps->size].name);
printf("情输入年龄:>");
scanf("%d", &(ps->date[ps->size].age));
printf("情输入性别:>");
scanf("%s", ps->date[ps->size].sex);
printf("情输入电话:>");
scanf("%s", ps->date[ps->size].tele);
printf("情输入地址:>");
scanf("%s", ps->date[ps->size].addr);
ps->size++;
printf("录入信息完成\n");
}

void Showcontact(const struct contact* ps)
{
assert(ps != NULL);
int i = 0;
if (ps->size == 0)
{
printf("通讯录内容为空,无法打印\n");
}
else
{
for (i = 0; i < ps->size; i++)
{
printf("%-20s\t%-3s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-3d\t%-5s\t%-12s\t%-20s\n",
ps->date[i].name,
ps->date[i].age,
ps->date[i].sex,
ps->date[i].tele,
ps->date[i].addr);
}
}
}

static int Find_by_name(const struct contact ps, char name[MAX_NAME])
{//static修饰可以使其函数仅可以再本。c生效
//避免了污染
assert(ps != NULL);
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (strcmp(ps->date[i].name, name) == 0)
return i;
}
return -1;
}
void Delcontact(struct contact
ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要删除联系人的姓名:>");
scanf("%s", name);
//查找
//找到返回pos下标
//找不到返回-1
int pos=Find_by_name(ps, name);
if (pos == -1)
{
printf("要删除的联系人不存在\n");
}
else
{
int j = 0;
for (j = pos; j size ; j++)//
//*
{
ps->date[j] = ps->date[j + 1];
}
ps->size--;
printf("删除成功\n");
}
}

void Searchcontact(const struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要查找联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要查找的联系人不存在\n");
}
else
{
printf("信息如下:\n");
printf("%-20s\t%-3s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-3d\t%-5s\t%-12s\t%-20s\n",
ps->date[pos].name,
ps->date[pos].age,
ps->date[pos].sex,
ps->date[pos].tele,
ps->date[pos].addr);
}
}

void Modifycontact(struct contact* ps)
{
assert(ps != NULL);
char name[MAX_NAME];
printf("请输入要修改联系人的姓名:>");
scanf("%s", name);
int pos = Find_by_name(ps, name);
if (pos == -1)
{
printf("要修改的联系人不存在\n");
}
else
{
int input = 0;
do
{
printf("请输入你想修改此用户的哪项信息\n");
printf(" 0.退出 1.姓名 2.年龄 3.性别 4.电话 5.地址:>\n");
scanf("%d", &input);
switch (input)
{
case NAME:
printf("情输入姓名:>");
scanf("%s", ps->date[pos].name);
break;
case AGE:
printf("情输入年龄:>");
scanf("%d", &(ps->date[pos].age));
break;
case SEX:
printf("情输入性别:>");
scanf("%s", ps->date[pos].sex);
break;
case TELE:
printf("情输入电话:>");
scanf("%s", ps->date[pos].tele);
break;
case ADDR:
printf("情输入地址:>");
scanf("%s", ps->date[pos].addr);
break;
case ERRO:
printf("修改完成\n");
break;
default:
printf("输入错误,请重新输入:>\n");
break;
}
} while (input);
}
}

int con_name(const void e1, const void e2)
{
return (strcmp(((struct contact)e1)->date->name, ((struct contact)e2)->date->name));
}
void SortContact(struct contact* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("通讯录为空,无法排序\n");
return;
}
qsort(ps->date, ps->size, sizeof(ps->date[0]), con_name);
printf("排序成功\n");
Showcontact(ps);
}

void SaveContact(struct contact ps)
{
FILE
pfwrite = fopen("contact.dat", "wb");
if (pfwrite == NULL)
{
printf("SaveContact::%s", strerror(errno));
return;
}
int i = 0;
for (i = 0; i < ps->size; i++)
{
fwrite(&(ps->date[i]), sizeof(struct PeoInfo), 1, pfwrite);
}
fclose(pfwrite);
pfwrite = NULL;
}

void DestroyContact(struct contact* ps)
{
assert(ps);
free(ps->date);
ps->date = NULL;
ps->size = 0;
printf("销毁成功\n");
}

contact.h(优化版)

define _CRT_SECURE_NO_WARNINGS

include

include

include

include

include

define MAX_NAME 20

define MAX_SEX 5

define MAX_TELE 12

define MAX_ADDR 30

define CAPACITY 3

enum option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT,
SAVE,
};
enum Information
{
ERRO,
NAME,
AGE,
SEX,
TELE,
ADDR,
};

struct PeoInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};

struct contact
{
struct PeoInfo *date;
int size;
int capacity;
};

void Initcon(struct contact* ps);

void Addcontact(struct contact* ps);

void Showcontact(const struct contact* ps);

void Delcontact(struct contact* ps);

void Searchcontact(const struct contact* ps);

void Modifycontact(struct contact* ps);

void SortContact(struct contact* ps);

void DestroyContact(struct contact* ps);

void SaveContact(struct contact* ps);

void loadcontact(struct contact* ps);

感谢阅读!!!

目录
相关文章
|
Cloud Native Go 开发工具
不改一行代码轻松玩转 Go 应用微服务治理
为了更好的进行 Go 应用微服务治理,提高研发效率和系统稳定性,本文将介绍 MSE 微服务治理方案,无需修改业务代码,实现治理能力。
20135 96
|
6月前
|
存储 缓存 Java
c++的哈希表、哈希桶的介绍与实现
这一篇文章大致实现详细介绍什么是哈希,然后再介绍什么是哈希表,怎么代码实现哈希表,然后再介绍哈希桶,怎么代码实现哈希桶,最后再介绍他俩有什么细节上的差别,与代码的一些细节优化。
130 0
|
6月前
|
NoSQL Linux 开发工具
Linux环境基础开发工具的使用(yum、vim、gcc、g++、gdb、make/Makefile)
本文介绍了yum 包管理工具、Vim 编辑器、gcc/g++ 编译器、gdb 调试器、编译原理及 Makefile 的使用,同时还配备了如何使用,以及图解。旨在帮助读者更好地理解和应用这些工具与技术。
335 0
|
6月前
|
存储 安全 Java
c++--继承
c++作为面向对象的语言三大特点其中之一就是继承,那么继承到底有何奥妙呢?继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用,继承就是类方法的复用。
144 0
|
6月前
|
容器
定长滑动窗口--灵神题单(一刷进阶)
这篇文章主要围绕灵神题单中的定长滑动窗口进阶题进行探讨,首先解决了一些基本题目,然后附带了一些相对困难题目的解题思路。值得注意的是,大部分解答都是在前人的基础上进行了一些小的修改和调整。希望文章对大家的学习和解题能力有所帮助。 如果您需要更具体的内容或者对某一部分有兴趣深入讨论,请告诉我!
117 0
|
6月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
187 0
|
6月前
|
存储 编译器 容器
set、map、multiset、multimap的介绍及使用以及区别,注意事项
set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列。set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重。set默认是升序的,但是其内部默认不是按照大于比较,而是按照小于比较。set中的元素不能被修改,因为set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树。
248 0
|
消息中间件 测试技术 Kafka
Apache RocketMQ 批处理模型演进之路
RocketMQ 早期批处理模型存在一定的约束条件,为进一步提升性能,RocketMQ 进行了索引构建流水线改造,同时 BatchCQ 模型和 AutoBatch 模型也优化了批处理流程,提供了更简便的使用体验,快点击本文查看详情及配置展示~
19989 158
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19705 30
|
机器学习/深度学习 算法
【文献学习】Channel Estimation Method Based on Transformer in High Dynamic Environment
一种基于CNN和Transformer的信道估计方法,用于在高度动态环境中跟踪信道变化特征,并通过实验结果展示了其相比传统方法的性能提升。
280 0