加油加油!!!

文章目录

  • 前言
  • 一:静态库通讯录
    • 1:通讯录介绍
      • 1.1代码主体框架的介绍
      • 1.2 要实现的功能
    • 2:通讯录实现
      • 2.1 通讯录的外部封装
      • 2.1 创建通讯录
      • 2.2 初始化通讯录
      • 2.3 增加联系人(功能1)
      • 2.4 展示通讯录(功能6)
      • 2.5 删除指定联系人(功能2)
      • 2.6 查找指定联系人(功能4)
      • 2.7 修改指定联系人信息(功能3)
      • 2.8 排序通讯录(功能5)
      • 2.9 功能组装
    • 3: 源码展示
  • 二:动态库通讯录
    • 1 创建通讯录
    • 2 初始化通讯录
    • 3 增加通讯录
    • 4 销毁通讯录
    • 5: 源码展示
  • 三:文件通讯录
    • 1:保存通信录
    • 2:加载通信录
    • 3:程序源码

前言

本文主要利用C语言结构体和指针的知识实现通讯录。我们先将静态的通讯录实现,再进行改良,用动态内存的知识再将通讯录改造一边,将动态内容的知识也运用一下,最后再用文件操作的方式在改造一下通讯录。

1:静态版本通讯录
2:动态版本通讯录
3:文件版本通讯录


一:静态库通讯录

1:通讯录介绍

在写代码前我们需要考虑两件事:1:代码的框架2:要实现的功能

1.1代码主体框架的介绍

代码的主题框架包含三个部分test.ccontact.ccontact.h

test:测试通讯录的实现,也就是主函数,整个代码的操作逻辑
contact.c:通讯录中函数的实现
contact.h:通讯录中函数和结构体等的声明

1.2 要实现的功能

1:我们要实现一个可以存放100个人的信息的通讯录,每个人的信息包括(姓名,性别,电话,年龄,住址
2:通讯录还要有以下功能,
(1)增加联系人
(2)删除指定联系人
(3)修改指定联系人
(4)查找指定联系人
(5)排序
(6)显示通讯录的信息

2:通讯录实现

2.1 通讯录的外部封装

这一步很常规,和之前的三子棋、扫雷一样均采用do-while循环结构内嵌套switch-case语句,同时需要写一个菜单函数提示用户进行选择。

这类小项目通用的外部封装代码如下:

#include<stdio.h>
//菜单函数,根据需求进行修改即可
void menu()
{printf("*************************************\n");printf("******   1.         2.         ******\n");printf("******   3.         4.         ******\n");printf("******   5.         6.         ******\n");printf("******   0.ecit                ******\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;
}

对于本通讯录而言,外部封装代码如下在(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.1 创建通讯录

<创建一个结构体>
想要创建一个通讯录,就要创建一个结构体,用来表示一个人的信息(姓名,性别,电话,年龄,住址),而通讯录就想相当于众多人的集合,所以我们要创建一个结构体数组来存放这些人

而对于这个表示一个人信息的结构体,因为也只是一个结构体,我们就放在通讯录声明的文件当中,也就是 contact.h文件。

//创建一个结构体,表示一个人的信息
struct PeoInfo
{char name[20];char sex[20];char tele[12];int age;char addr[30];
};

<创建一个通讯录>
1:创建好这个记录每个人信息的结构体,我们现在就可以创建通讯录了。通讯录就是这个结构体数组。放到代码就是 struct PeoInfo data[100];这样就创建了可以存放100个人信息的通讯录。
2:但是这样就完事了吗?显然不不行。例如如果你想要增加一个信息,你要增加那个位置,你是不知道的,所以对于通讯录来说,我们不仅要创建这个数组,也要知道这个通讯录有几个信息,这时我们创建一个int sz; 来记录通讯录里现在有几个信息。
比如如果现在由sz个人,那我就把信息放在sz+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact

代码如下,因为这是封装一个通讯录结构体,所以也放在 contact.h文件中。

//创建通讯录
struct Contact
{struct PeoInfo data[100];  //存放每个人信息的数组int sz;                 //要存放的位置 这两个加起来才是我们完整的通讯录,故再创建一个结构体
};

故在主函数中构建一个通讯录


<创建宏>
为了便于后续添加和代码的一直,我们统一将各个数组的大小进行宏定义

#define MAX          100
#define MAX_NAME    20
#define MAX_SEX     5
#define MAX_TELE    12
#define MAX_ADDR    30

2.2 初始化通讯录

对于创建好了通讯录,对于通讯录的内容,应该先初始化,之后才可以进行修改。这就涉及了结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为要对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参。

方式1:一个一个初始化(有点呆,不推荐)

void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
{assert(pc);int i = 0;//初始化姓名for(i=0;i<20;i++){(pc->data)->name[i] = '0';}//初始化性别for (i = 0; i < 5; i++){(pc->data)->sex[i] = '0';}//初始化电话for (i = 0; i < 12; i++){(pc->data)->tele[i] = 0;}//初始化年龄(pc->data)->age = 0;//初始化地址for (i = 0; i < 30; i++){(pc->data)->addr[i] = 0;}//初始化szpc->sz = 0;
}

方式2:采用memset初始化(推荐)

void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
{assert(pc);memset(pc->data, 0, sizeof(struct PeoInfo) * MAX);   //data是一个结构体,初始化可以采用memsetpc->sz = 0;
}

2.3 增加联系人(功能1)

注意:增加联系人前要判断通讯录有没有满

void AddContact(struct Contact* pc)
{assert(pc);  //断言,防止pc为空指针if (pc->sz == MAX){printf("通讯录满了,无法添加数据\n");return;}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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("成功添加联系人\n");
}

2.4 展示通讯录(功能6)

我们对通讯录进行操作后想看一看是否对通讯录操作成功,所以需要展示一下通讯录
但是为了美观我们希望在展示前加上表头姓名,性别,电话,年龄,住址),同时为了使之间有空隙我们采用水平制表符\t

//展示通讯录
void ShowContact(const struct Contact* pc)
{assert(pc);int i = 0;printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-12s\t%-2d\t%-30s\n", pc->data[i].name,pc->data[i].sex,pc->data[i].tele,pc->data[i].age,pc->data[i].addr);}
}

注意:第一个printf()的年龄那里也是%s,因为我们打印的是字符串"年龄"。

结果如下:

2.5 删除指定联系人(功能2)

:不管是删除、查找、更改。都需要确定输入的联系人是否存在,故将其封装成一个函数,存在则返回位置,不存在返回-1。

//删除联系人
void DelContact(struct Contact* pc)
{assert(pc);char name[MAX_NAME];   //存放输出删除的名字printf("请输入要删除的联系人姓名:>");scanf("%s", name);//不管是删除、查找、更改。都需要找输入的联系人是否存在,故将其封装成一个函数,存在返回位置,不存在返回-1//1:查找int ret = FindByName(pc, name);if (ret == -1)printf("要删除的联系人不存在\n");else{//删除int j = 0;for (j = ret; j < (pc->sz - 1); j++)   //100个数据的数组,下标最大为99{pc->data[j] = pc->data[j + 1];  //将返回值后面的数据整体往前覆盖一位。使用memmove也可以 }//memmove(&(pc->data[ret]), &(pc->data[ret + 1]), pc->sz - (ret + 1));printf("成功删除指定联系人\n");pc->sz--;}
}

2.6 查找指定联系人(功能4)

//查找联系人
void SearchContact(const struct Contact* pc)
{assert(pc);char name[MAX_NAME];printf("请输入要查找的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要查找的联系人不存在\n");else{//打印printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零printf("%-20s\t%-5s\t%-12s\t%-2d\t%-30s\n", pc->data[ret].name,pc->data[ret].sex,pc->data[ret].tele,pc->data[ret].age,pc->data[ret].addr);}
}

2.7 修改指定联系人信息(功能3)

修改通讯录,就是先找到要修改的信息位置,然后再像增加通讯录一样重新录入一遍信息。

void ModifyContact(struct Contact* pc)
{assert(pc);char name[MAX_NAME];printf("请输入要修改的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要修改的联系人不存在\n");else{printf("请输入名字:>");scanf("%s", pc->data[ret].name);   //将ret位置的数据改了,重新输入一遍新的数据printf("请输入性别:>");scanf("%s", pc->data[ret].sex);printf("请输入电话:>");scanf("%s", pc->data[ret].tele);printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[ret].addr);printf("修改联系人成功\n");}
}

注意:上述代码是是对这个人的信息全部都重新输入了一遍,有时候我们只需要修改某一个地方,比如:只修改地址,如果全部输入的话,有些麻烦。于是,我们可以再创建一个二级菜单,提示用户需要修改的信息,只需要选择并且输入修改信息即可。

优化修改功能代码如下:

//可以指定修改信息!!!
//人物信息菜单
static void PeoMenu()
{printf("*************************************\n");printf("******   1.姓名      2.性别     ******\n");printf("******   3.电话      4.年龄     ******\n");printf("******   5.地址      0.退出    ******\n");printf("*************************************\n");
}void ModifyContact(struct Contact* pc)
{assert(pc);int input1;   //需要修改的数据char name[MAX_NAME];printf("请输入要修改的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要修改的联系人不存在\n");else{do{PeoMenu();printf("请选择:>");scanf("%d", &input1);switch (input1){case 1:printf("请输入名字:>");scanf("%s", pc->data[ret].name);break;case 2:printf("请输入性别:>");scanf("%s", pc->data[ret].sex);break;case 3:printf("请输入电话:>");scanf("%s", pc->data[ret].tele);break;case 4:printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));break;case 5:printf("请输入地址:>");scanf("%s", pc->data[ret].addr);break;case 0:printf("修改完成,退出修改\n");break;default:printf("输入信息有误,请重新输入\n");break;}} while (input1);printf("修改联系人成功\n");}
}

2.8 排序通讯录(功能5)

排序我们使用库函数qsort(),关于此函数的详细用法,可参考这篇文章
参考链接:《用冒泡排序模拟qsort库函数》

//按照年龄排序
static int CmpByAge(const void* e1, const void* e2)
{return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}//排序指定方式,可以排序任意字符串类型,修改宏即可
static int CmpAnyWay(const void* e1, const void* e2)
{return strcmp(((struct PeoInfo*)e1)->SortWay,((struct PeoInfo*)e2)->SortWay);
}//排序通讯录
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);     //按照年龄排序qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAnyWay);   //按照名字排序ShowContact(pc);printf("按照名字排序成功\n");
}

对于任意方式排序只需要修改contact.h中的宏即可!

2.9 功能组装

将所有的功能函数对应放到主函数的对应位置

int main()
{int input;//1:创建通讯录struct Contact con;  //通讯录          //struct PeoInfo data[100];  //存放每个人信息的数组//int sz = 0;  //记录通讯录里现在有几个信息。比如如果现在由n个人,那我就把信息放在n+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact//2:初始化通讯录InitContact(&con);printf("\t这是一个通讯录的小程序\n");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:SearchContact(&con);break;case 5:SortContact(&con);break;case 6:ShowContact(&con);break;case 0:printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}//Sleep(1000);//system("cls");  //清屏} while (input);
};

3: 源码展示

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()
{int input;//1:创建通讯录struct Contact con;  //通讯录          //struct PeoInfo data[100];  //存放每个人信息的数组//int sz = 0;  //记录通讯录里现在有几个信息。比如如果现在由n个人,那我就把信息放在n+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact//2:初始化通讯录InitContact(&con);printf("\t这是一个通讯录的小程序\n");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:SearchContact(&con);break;case 5:SortContact(&con);break;case 6:ShowContact(&con);break;case 0:printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}//Sleep(1000);//system("cls");  //清屏} while (input);
};

contact.c

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"//方式1:一个一个初始化,对于数组则需要遍历才能初始化,方法很呆!!!!!
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  int i = 0;
//  //初始化姓名
//  for(i=0;i<20;i++)
//  {//      (pc->data)->name[i] = '0';
//  }
//
//  //初始化性别
//  for (i = 0; i < 5; i++)
//  {//      (pc->data)->sex[i] = '0';
//  }
//
//  //初始化电话
//  for (i = 0; i < 12; i++)
//  {//      (pc->data)->tele[i] = 0;
//  }
//
//  //初始化年龄
//  (pc->data)->age = 0;
//
//  //初始化地址
//  for (i = 0; i < 30; i++)
//  {//      (pc->data)->addr[i] = 0;
//  }
//
//  //初始化sz
//  pc->sz = 0;
//}//初始化通讯录
void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
{assert(pc);memset(pc->data, 0, sizeof(struct PeoInfo) * MAX);   //data是一个结构体,初始化可以采用memsetpc->sz = 0;
}//增加联系人
void AddContact(struct Contact* pc)
{assert(pc);  //断言,防止pc为空指针if (pc->sz == MAX){printf("通讯录满了,无法添加数据\n");return;}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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("成功添加联系人\n");
}//展示通讯录
void ShowContact(const struct Contact* pc)
{assert(pc);int i = 0;printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-12s\t%-2d\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 FindByName(const struct Contact* pc, char name[])  //因为不需要修改,所以采用const修饰
{assert(pc);//查找,要数组元素遍历int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;}}return -1;
}//删除联系人
void DelContact(struct Contact* pc)
{assert(pc);char name[MAX_NAME];   //存放输出删除的名字printf("请输入要删除的联系人姓名:>");scanf("%s", name);//不管是删除、查找、更改。都需要找输入的联系人是否存在,故将其封装成一个函数,存在返回位置,不存在返回-1//1:查找int ret = FindByName(pc, name);if (ret == -1)printf("要删除的联系人不存在\n");else{//删除int j = 0;for (j = ret; j < (pc->sz - 1); j++)   //100个数据的数组,下标最大为99{pc->data[j] = pc->data[j + 1];  //将返回值后面的数据整体往前覆盖一位。使用memmove也可以 }//memmove(&(pc->data[ret]), &(pc->data[ret + 1]), pc->sz - (ret + 1));printf("成功删除指定联系人\n");pc->sz--;}
}//查找联系人
void SearchContact(const struct Contact* pc)
{assert(pc);char name[MAX_NAME];printf("请输入要查找的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要查找的联系人不存在\n");else{//打印printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零printf("%-20s\t%-5s\t%-12s\t%-2d\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)
//{//  assert(pc);
//
//  char name[MAX_NAME];
//  printf("请输入要修改的联系人姓名:>");
//  scanf("%s", name);
//
//  //查找
//  int ret = FindByName(pc, name);
//  if (ret == -1)
//      printf("要修改的联系人不存在\n");
//  else
//  {//      printf("请输入名字:>");
//      scanf("%s", pc->data[ret].name);   //将ret位置的数据改了,重新输入一遍新的数据
//      printf("请输入性别:>");
//      scanf("%s", pc->data[ret].sex);
//      printf("请输入电话:>");
//      scanf("%s", pc->data[ret].tele);
//      printf("请输入年龄:>");
//      scanf("%d", &(pc->data[ret].age));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//      printf("请输入地址:>");
//      scanf("%s", pc->data[ret].addr);
//
//      printf("修改联系人成功\n");
//  }
//}/***************************************************************************************************************************/
//可以指定修改信息!!!
//人物信息菜单
static void PeoMenu()
{printf("*************************************\n");printf("******   1.姓名      2.性别     ******\n");printf("******   3.电话      4.年龄     ******\n");printf("******   5.地址      0.退出    ******\n");printf("*************************************\n");
}void ModifyContact(struct Contact* pc)
{assert(pc);int input1;   //需要修改的数据char name[MAX_NAME];printf("请输入要修改的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要修改的联系人不存在\n");else{do{PeoMenu();printf("请选择:>");scanf("%d", &input1);switch (input1){case 1:printf("请输入名字:>");scanf("%s", pc->data[ret].name);break;case 2:printf("请输入性别:>");scanf("%s", pc->data[ret].sex);break;case 3:printf("请输入电话:>");scanf("%s", pc->data[ret].tele);break;case 4:printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));break;case 5:printf("请输入地址:>");scanf("%s", pc->data[ret].addr);break;case 0:printf("修改完成,退出修改\n");break;default:printf("输入信息有误,请重新输入\n");break;}} while (input1);printf("修改联系人成功\n");}
}
/***************************************************************************************************************************///按照年龄排序
static int CmpByAge(const void* e1, const void* e2)
{return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}//排序指定方式,可以排序任意字符串类型,修改宏即可
static int CmpAnyWay(const void* e1, const void* e2)
{return strcmp(((struct PeoInfo*)e1)->SortWay,((struct PeoInfo*)e2)->SortWay);
}//排序通讯录
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);     //按照年龄排序qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAnyWay);   //按照名字排序ShowContact(pc);printf("按照名字排序成功\n");
}

contact.h

#pragma once#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <assert.h>
#include <stdlib.h>#define MAX            100
#define MAX_NAME    20
#define MAX_SEX     5
#define MAX_TELE    12
#define MAX_ADDR    30//修改宏,可以按照任意方式排序
#define SortWay name//每个人的信息
struct PeoInfo
{char name[MAX_NAME];char sex[MAX_SEX];   //汉字占两个字节,还有一个\0占一个字节char tele[MAX_TELE];int age;char addr[MAX_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);  //只是为了展示,不会通过pc来改变数据//删除联系人
void DelContact(struct Contact* pc);//查找联系人
void SearchContact(const struct Contact* pc);//修改联系人
void ModifyContact(struct Contact* pc);//排序通讯录
void SortContact(struct Contact* pc);

以上就是静态库版本通讯录的全部内容了。

二:动态库通讯录

  • 为什么要设计动态库通讯录?

    静态库不够灵活,比如如果我想加入101个人,但是静态通讯录的大小是固定的,只能存放100个人,这个时候还需要手动改大小。又比如,我只想存放10个人,但是你给通讯录开辟的大小是100人的,就会造成空间上的浪费

  • 设计动态通讯录的思路

    首先创建一个通讯了,这里的创建和之前的有所区别,下面会一一讲解,其次使用malloc申请可以存放3个人的空间,作为通讯录起始的默认空间大小,存放满后,使用realloc增加两个人的空间,如此往复。这样就更加灵活,并且不会浪费空间。

    对于malloc创建空间,这里需要改变一下,因为malloc返回的是void*的指针,所以将 data[]变为指针。同时,需要多增加一个参数,容量参数 capacity,当 szcapacity相同时,增加2个空间。

1 创建通讯录

//动态库版本
//创建通讯录
struct Contact
{struct PeoInfo *data;  //存放每个人信息int sz;                    //已经放进去的人数int capacity;         //容量
};

2 初始化通讯录

void InitContact(struct Contact* pc)
{assert(pc);pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));if (pc->data == NULL){perror("InitContact");return;}else{//使用pc->sz = 0;pc->capacity = DEFAULT_SZ;}
}

3 增加通讯录


//检测容量够不够,不够了就扩容,返回1,够的话,也返回1,不需要扩容
//扩容失败,返回0
static int check_capacity(struct Contact* pc)
{if (pc->sz == pc->capacity){//扩容//这里不能直接赋值给pc->data1,因为可能会增容失败struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (INC_SZ + pc->capacity) * sizeof(struct PeoInfo));  if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;printf("扩容成功\n");return 1;}else{perror("AddContact");return 0;}}elsereturn 1;  //不需要扩容
}//动态版本
//增加联系人
void AddContact(struct Contact* pc)
{assert(pc);  //断言,防止pc为空指针int ret = check_capacity(pc);if (ret == 0){return;  //扩容失败,直接返回,后面的就不执行了}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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("成功添加联系人\n");}
}

4 销毁通讯录

既然是动态内存,如果用完了,我们就要将内存还给操作系统。我们知道callocfree是同时出现的,申请了空间,就肯定要销毁空间,所以在退出函数就可以添加一个函数,销毁函数。

//销毁通讯录-EXIT
void DestoryContact(struct Contact* pc)
{free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;
}

5: 源码展示

test.c

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"void menu()
{printf("**************************************\n");printf("*****   1. add      2. del       *****\n");printf("*****   3. modify   4. search    *****\n");printf("*****   5. sort     6. show      *****\n");printf("*****   0. exit                  *****\n");printf("**************************************\n");
}enum Option
{EXIT,   //默认值为0,一次递增ADD,DEL,MODIFY,SEARCH,SORT,SHOW
};//int main()
//{//  int input;
//  //1:创建通讯录
//  struct Contact con;  //通讯录          //struct PeoInfo data[100];  //存放每个人信息的数组
//                                          //int sz = 0;  //记录通讯录里现在有几个信息。比如如果现在由n个人,那我就把信息放在n+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact
//  //2:初始化通讯录
//  InitContact(&con);
//  printf("\t这是一个通讯录的小程序\n");
//  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:
//          SearchContact(&con);
//          break;
//      case 5:
//          SortContact(&con);
//          break;
//      case 6:
//          ShowContact(&con);
//          break;
//      case 0:
//          printf("退出通讯录\n");
//          break;
//      default:
//          printf("选择错误,请重新选择\n");
//          break;
//      }
//      //Sleep(1000);
//      //system("cls");  //清屏
//  } while (input);
//};//使用枚举将数字0123456替换后
int main()
{int input;//1:创建通讯录struct Contact con;  //通讯录          //struct PeoInfo data[100];  //存放每个人信息的数组//int sz = 0;  //记录通讯录里现在有几个信息。比如如果现在由n个人,那我就把信息放在n+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact//2:初始化通讯录InitContact(&con);printf("\t这是一个通讯录的小程序\n");do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case MODIFY:ModifyContact(&con);break;case SEARCH:SearchContact(&con);break;case SORT:SortContact(&con);break;case SHOW:ShowContact(&con);break;case EXIT:DestoryContact(&con);printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}//Sleep(1000);//system("cls");  //清屏} while (input);
};

contact.c

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"//方式1:一个一个初始化,对于数组则需要遍历才能初始化,方法很呆!!!!!
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  int i = 0;
//  //初始化姓名
//  for(i=0;i<20;i++)
//  {//      (pc->data)->name[i] = '0';
//  }
//
//  //初始化性别
//  for (i = 0; i < 5; i++)
//  {//      (pc->data)->sex[i] = '0';
//  }
//
//  //初始化电话
//  for (i = 0; i < 12; i++)
//  {//      (pc->data)->tele[i] = 0;
//  }
//
//  //初始化年龄
//  (pc->data)->age = 0;
//
//  //初始化地址
//  for (i = 0; i < 30; i++)
//  {//      (pc->data)->addr[i] = 0;
//  }
//
//  //初始化sz
//  pc->sz = 0;
//}//静态版本
//方式2:初始化通讯录,使用memset
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  assert(pc);
//  memset(pc->data, 0, sizeof(struct PeoInfo) * MAX);   //data是一个结构体,初始化可以采用memset
//  pc->sz = 0;
//}//动态版本
//初始化通讯录
void InitContact(struct Contact* pc)
{assert(pc);pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));if (pc->data == NULL){perror("InitContact");return;}else{//使用pc->sz = 0;pc->capacity = DEFAULT_SZ;}
}//销毁通讯录-EXIT
void DestoryContact(struct Contact* pc)
{free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;
}//静态版本
//增加联系人
//void AddContact(struct Contact* pc)
//{//  assert(pc);  //断言,防止pc为空指针
//  if (pc->sz == MAX)
//  {//      printf("通讯录满了,无法添加数据\n");
//      return;
//  }
//  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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//  printf("请输入地址:>");
//  scanf("%s", pc->data[pc->sz].addr);
//
//  pc->sz++;
//  printf("成功添加联系人\n");
//}//检测容量够不够,不够了就扩容,返回1,够的话,也返回1,不需要扩容
//扩容失败,返回0
static int check_capacity(struct Contact* pc)
{if (pc->sz == pc->capacity){//扩容//这里不能直接赋值给pc->data1,因为可能会增容失败struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (INC_SZ + pc->capacity) * sizeof(struct PeoInfo));  if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;printf("扩容成功\n");return 1;}else{perror("AddContact");return 0;}}elsereturn 1;  //不需要扩容
}//动态版本
//增加联系人
void AddContact(struct Contact* pc)
{assert(pc);  //断言,防止pc为空指针int ret = check_capacity(pc);if (ret == 0){return;  //扩容失败,直接返回,后面的就不执行了}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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("成功添加联系人\n");}
}//展示通讯录
void ShowContact(const struct Contact* pc)
{assert(pc);int i = 0;printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-12s\t%-2d\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 FindByName(const struct Contact* pc, char name[])  //因为不需要修改,所以采用const修饰
{assert(pc);//查找,要数组元素遍历int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;}}return -1;
}//删除联系人
void DelContact(struct Contact* pc)
{assert(pc);char name[MAX_NAME];   //存放输出删除的名字printf("请输入要删除的联系人姓名:>");scanf("%s", name);//不管是删除、查找、更改。都需要找输入的联系人是否存在,故将其封装成一个函数,存在返回位置,不存在返回-1//1:查找int ret = FindByName(pc, name);if (ret == -1)printf("要删除的联系人不存在\n");else{//删除int j = 0;for (j = ret; j < (pc->sz - 1); j++)   //100个数据的数组,下标最大为99{pc->data[j] = pc->data[j + 1];  //将返回值后面的数据整体往前覆盖一位。使用memmove也可以 }//memmove(&(pc->data[ret]), &(pc->data[ret + 1]), pc->sz - (ret + 1));printf("成功删除指定联系人\n");pc->sz--;}
}//查找联系人
void SearchContact(const struct Contact* pc)
{assert(pc);char name[MAX_NAME];printf("请输入要查找的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要查找的联系人不存在\n");else{//打印printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零printf("%-20s\t%-5s\t%-12s\t%-2d\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)
//{//  assert(pc);
//
//  char name[MAX_NAME];
//  printf("请输入要修改的联系人姓名:>");
//  scanf("%s", name);
//
//  //查找
//  int ret = FindByName(pc, name);
//  if (ret == -1)
//      printf("要修改的联系人不存在\n");
//  else
//  {//      printf("请输入名字:>");
//      scanf("%s", pc->data[ret].name);   //将ret位置的数据改了,重新输入一遍新的数据
//      printf("请输入性别:>");
//      scanf("%s", pc->data[ret].sex);
//      printf("请输入电话:>");
//      scanf("%s", pc->data[ret].tele);
//      printf("请输入年龄:>");
//      scanf("%d", &(pc->data[ret].age));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//      printf("请输入地址:>");
//      scanf("%s", pc->data[ret].addr);
//
//      printf("修改联系人成功\n");
//  }
//}/***************************************************************************************************************************/
//可以指定修改信息!!!
//人物信息菜单
static void PeoMenu()
{printf("*************************************\n");printf("******   1.姓名      2.性别     ******\n");printf("******   3.电话      4.年龄     ******\n");printf("******   5.地址      0.退出    ******\n");printf("*************************************\n");
}void ModifyContact(struct Contact* pc)
{assert(pc);int input1;   //需要修改的数据char name[MAX_NAME];printf("请输入要修改的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要修改的联系人不存在\n");else{do{PeoMenu();printf("请选择:>");scanf("%d", &input1);switch (input1){case 1:printf("请输入名字:>");scanf("%s", pc->data[ret].name);break;case 2:printf("请输入性别:>");scanf("%s", pc->data[ret].sex);break;case 3:printf("请输入电话:>");scanf("%s", pc->data[ret].tele);break;case 4:printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));break;case 5:printf("请输入地址:>");scanf("%s", pc->data[ret].addr);break;case 0:printf("修改完成,退出修改\n");break;default:printf("输入信息有误,请重新输入\n");break;}} while (input1);printf("修改联系人成功\n");}
}
/***************************************************************************************************************************///按照年龄排序
static int CmpByAge(const void* e1, const void* e2)
{return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}//排序指定方式,可以排序任意字符串类型,修改宏即可
static int CmpAnyWay(const void* e1, const void* e2)
{return strcmp(((struct PeoInfo*)e1)->SortWay,((struct PeoInfo*)e2)->SortWay);
}//排序通讯录
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);     //按照年龄排序qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAnyWay);   //按照名字排序ShowContact(pc);printf("按照名字排序成功\n");
}

contact.h

#pragma once#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <assert.h>
#include <stdlib.h>#define MAX            100
#define MAX_NAME    20
#define MAX_SEX     5
#define MAX_TELE    12
#define MAX_ADDR    30//修改宏,可以按照任意方式排序
#define SortWay name#define DEFAULT_SZ 3   //初始化后,默认通讯录的的大小
#define INC_SZ 3   //通讯录每次增加的大小//每个人的信息
struct PeoInfo
{char name[MAX_NAME];char sex[MAX_SEX];   //汉字占两个字节,还有一个\0占一个字节char tele[MAX_TELE];int age;char addr[MAX_ADDR];
};静态库版本
创建通讯录
//struct Contact
//{//  struct PeoInfo data[MAX];  //存放每个人信息的数组
//  int sz;                 //要存放的位置  ,这两个加起来才是我们完整的通讯录,故再创建一个结构体
//};//动态库版本
//创建通讯录
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);  //只是为了展示,不会通过pc来改变数据//删除联系人
void DelContact(struct Contact* pc);//查找联系人
void SearchContact(const struct Contact* pc);//修改联系人
void ModifyContact(struct Contact* pc);//排序通讯录
void SortContact(struct Contact* pc);//销毁通讯录
void DestoryContact(struct Contact* pc);

以上就是动态通讯录的全部内容了

三:文件通讯录

  • 文件通讯录需求
    通讯录退出后,之前输入的信息可以保存下来,下一次重新运行通讯库还可以看到上一次的信息
  • 分析需求
    推出的时候将通讯录保存下来,当下一次运行的时候,再从文件中加载信息

1:保存通信录

主要使用fopenfwrite,fclose函数

//保存通讯录
void SaveContact(struct Contact* pc)
{FILE* pfwrite = fopen("test.txt", "wb");if (pfwrite == NULL){perror("SaveContact:fopen");return;}//写数据int i = 0;for (i = 0; i < pc->sz; i++){//二进制写fwrite(pc->data+i, sizeof(struct PeoInfo), 1, pfwrite);   //每次写入一个结构体数据}//关闭文件fclose(pfwrite);pfwrite = NULL;}

2:加载通信录

要是的下次打开前通讯录就已经要保存的文件里读取信息了,需要将函数设置在初始化通讯录里

//加载通讯录
void LoadContact(struct Contact* pc)
{FILE* pfread = fopen("test.txt", "rb");if (pfread == NULL){perror("LoadContact:fopen");return;}//读数据//fread的返回值是size_t,返回成功读取的元素总数,失败则可能是发生了读取错误,不进入循环//二进制读struct PeoInfo temp = { 0 };while (fread(&temp, sizeof(struct PeoInfo), 1, pfread))    //使用fread需要用一个结构体来接收{//考虑增容check_capacity(pc);pc->data[pc->sz] = temp;   //将每次读取的数据放到指定位置上,方便查找和维护pc->sz++;}//关闭文件fclose(pfread);pfread = NULL;
}

3:程序源码

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");
}enum Option
{EXIT,   //默认值为0,一次递增ADD,DEL,MODIFY,SEARCH,SORT,SHOW
};//int main()
//{//  int input;
//  //1:创建通讯录
//  struct Contact con;  //通讯录          //struct PeoInfo data[100];  //存放每个人信息的数组
//                                          //int sz = 0;  //记录通讯录里现在有几个信息。比如如果现在由n个人,那我就把信息放在n+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact
//  //2:初始化通讯录
//  InitContact(&con);
//  printf("\t这是一个通讯录的小程序\n");
//  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:
//          SearchContact(&con);
//          break;
//      case 5:
//          SortContact(&con);
//          break;
//      case 6:
//          ShowContact(&con);
//          break;
//      case 0:
//          printf("退出通讯录\n");
//          break;
//      default:
//          printf("选择错误,请重新选择\n");
//          break;
//      }
//      //Sleep(1000);
//      //system("cls");  //清屏
//  } while (input);
//};//使用枚举将数字0123456替换后
int main()
{int input;//1:创建通讯录struct Contact con;  //通讯录          //struct PeoInfo data[100];  //存放每个人信息的数组//int sz = 0;  //记录通讯录里现在有几个信息。比如如果现在由n个人,那我就把信息放在n+1的位置上。这两个加起来才是我们完整的通讯录,故需要再创建一个结构体Contact//2:初始化通讯录InitContact(&con);printf("\t这是一个通讯录的小程序\n");do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case MODIFY:ModifyContact(&con);break;case SEARCH:SearchContact(&con);break;case SORT:SortContact(&con);break;case SHOW:ShowContact(&con);break;case EXIT:SaveContact(&con);DestoryContact(&con);printf("退出通讯录\n");break;default:printf("选择错误,请重新选择\n");break;}//Sleep(1000);//system("cls");  //清屏} while (input);
};

contact.c

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"//方式1:一个一个初始化,对于数组则需要遍历才能初始化,方法很呆!!!!!
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  int i = 0;
//  //初始化姓名
//  for(i=0;i<20;i++)
//  {//      (pc->data)->name[i] = '0';
//  }
//
//  //初始化性别
//  for (i = 0; i < 5; i++)
//  {//      (pc->data)->sex[i] = '0';
//  }
//
//  //初始化电话
//  for (i = 0; i < 12; i++)
//  {//      (pc->data)->tele[i] = 0;
//  }
//
//  //初始化年龄
//  (pc->data)->age = 0;
//
//  //初始化地址
//  for (i = 0; i < 30; i++)
//  {//      (pc->data)->addr[i] = 0;
//  }
//
//  //初始化sz
//  pc->sz = 0;
//}//静态版本
//方式2:初始化通讯录,使用memset
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  assert(pc);
//  memset(pc->data, 0, sizeof(struct PeoInfo) * MAX);   //data是一个结构体,初始化可以采用memset
//  pc->sz = 0;
//}//加载通讯录
void LoadContact(struct Contact* pc)
{FILE* pfread = fopen("test.txt", "rb");if (pfread == NULL){perror("LoadContact:fopen");return;}//读数据//fread的返回值是size_t,返回成功读取的元素总数,失败则可能是发生了读取错误,不进入循环//二进制读struct PeoInfo temp = { 0 };while (fread(&temp, sizeof(struct PeoInfo), 1, pfread))    //使用fread需要用一个结构体来接收{//考虑增容check_capacity(pc);pc->data[pc->sz] = temp;   //将每次读取的数据放到指定位置上,方便查找和维护pc->sz++;}//关闭文件fclose(pfread);pfread = NULL;
}//动态版本
//初始化通讯录
void InitContact(struct Contact* pc)
{assert(pc);pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));if (pc->data == NULL){perror("InitContact");return;}else{//使用pc->sz = 0;pc->capacity = DEFAULT_SZ;}LoadContact(pc);
}//销毁通讯录-EXIT
void DestoryContact(struct Contact* pc)
{free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;
}//静态版本
//增加联系人
//void AddContact(struct Contact* pc)
//{//  assert(pc);  //断言,防止pc为空指针
//  if (pc->sz == MAX)
//  {//      printf("通讯录满了,无法添加数据\n");
//      return;
//  }
//  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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//  printf("请输入地址:>");
//  scanf("%s", pc->data[pc->sz].addr);
//
//  pc->sz++;
//  printf("成功添加联系人\n");
//}//检测容量够不够,不够了就扩容,返回1,够的话,也返回1,不需要扩容
//扩容失败,返回0
int check_capacity(struct Contact* pc)
{if (pc->sz == pc->capacity){//扩容//这里不能直接赋值给pc->data1,因为可能会增容失败struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (INC_SZ + pc->capacity) * sizeof(struct PeoInfo));  if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;//printf("扩容成功\n");return 1;}else{perror("AddContact");return 0;}}elsereturn 1;  //不需要扩容
}//动态版本
//增加联系人
void AddContact(struct Contact* pc)
{assert(pc);  //断言,防止pc为空指针int ret = check_capacity(pc);if (ret == 0){return;  //扩容失败,直接返回,后面的就不执行了}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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("成功添加联系人\n");}
}//展示通讯录
void ShowContact(const struct Contact* pc)
{assert(pc);int i = 0;printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-12s\t%-2d\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 FindByName(const struct Contact* pc, char name[])  //因为不需要修改,所以采用const修饰
{assert(pc);//查找,要数组元素遍历int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;}}return -1;
}//删除联系人
void DelContact(struct Contact* pc)
{assert(pc);char name[MAX_NAME];   //存放输出删除的名字printf("请输入要删除的联系人姓名:>");scanf("%s", name);//不管是删除、查找、更改。都需要找输入的联系人是否存在,故将其封装成一个函数,存在返回位置,不存在返回-1//1:查找int ret = FindByName(pc, name);if (ret == -1)printf("要删除的联系人不存在\n");else{//删除int j = 0;for (j = ret; j < (pc->sz - 1); j++)   //100个数据的数组,下标最大为99{pc->data[j] = pc->data[j + 1];  //将返回值后面的数据整体往前覆盖一位。使用memmove也可以 }//memmove(&(pc->data[ret]), &(pc->data[ret + 1]), pc->sz - (ret + 1));printf("成功删除指定联系人\n");pc->sz--;}
}//查找联系人
void SearchContact(const struct Contact* pc)
{assert(pc);char name[MAX_NAME];printf("请输入要查找的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要查找的联系人不存在\n");else{//打印printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零printf("%-20s\t%-5s\t%-12s\t%-2d\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)
//{//  assert(pc);
//
//  char name[MAX_NAME];
//  printf("请输入要修改的联系人姓名:>");
//  scanf("%s", name);
//
//  //查找
//  int ret = FindByName(pc, name);
//  if (ret == -1)
//      printf("要修改的联系人不存在\n");
//  else
//  {//      printf("请输入名字:>");
//      scanf("%s", pc->data[ret].name);   //将ret位置的数据改了,重新输入一遍新的数据
//      printf("请输入性别:>");
//      scanf("%s", pc->data[ret].sex);
//      printf("请输入电话:>");
//      scanf("%s", pc->data[ret].tele);
//      printf("请输入年龄:>");
//      scanf("%d", &(pc->data[ret].age));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//      printf("请输入地址:>");
//      scanf("%s", pc->data[ret].addr);
//
//      printf("修改联系人成功\n");
//  }
//}/***************************************************************************************************************************/
//可以指定修改信息!!!
//人物信息菜单
static void PeoMenu()
{printf("*************************************\n");printf("******   1.姓名      2.性别     ******\n");printf("******   3.电话      4.年龄     ******\n");printf("******   5.地址      0.退出    ******\n");printf("*************************************\n");
}void ModifyContact(struct Contact* pc)
{assert(pc);int input1;   //需要修改的数据char name[MAX_NAME];printf("请输入要修改的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要修改的联系人不存在\n");else{do{PeoMenu();printf("请选择:>");scanf("%d", &input1);switch (input1){case 1:printf("请输入名字:>");scanf("%s", pc->data[ret].name);break;case 2:printf("请输入性别:>");scanf("%s", pc->data[ret].sex);break;case 3:printf("请输入电话:>");scanf("%s", pc->data[ret].tele);break;case 4:printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));break;case 5:printf("请输入地址:>");scanf("%s", pc->data[ret].addr);break;case 0:printf("修改完成,退出修改\n");break;default:printf("输入信息有误,请重新输入\n");break;}} while (input1);printf("修改联系人成功\n");}
}
/***************************************************************************************************************************///按照年龄排序
static int CmpByAge(const void* e1, const void* e2)
{return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}//排序指定方式,可以排序任意字符串类型,修改宏即可
static int CmpAnyWay(const void* e1, const void* e2)
{return strcmp(((struct PeoInfo*)e1)->SortWay,((struct PeoInfo*)e2)->SortWay);
}//排序通讯录
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);     //按照年龄排序qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAnyWay);   //按照名字排序ShowContact(pc);printf("按照名字排序成功\n");
}//保存通讯录
void SaveContact(struct Contact* pc)
{FILE* pfwrite = fopen("test.txt", "wb");if (pfwrite == NULL){perror("SaveContact:fopen");return;}//写数据int i = 0;for (i = 0; i < pc->sz; i++){//二进制写fwrite(pc->data+i, sizeof(struct PeoInfo), 1, pfwrite);   //每次写入一个结构体数据}//关闭文件fclose(pfwrite);pfwrite = NULL;}

contact.h

#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"//方式1:一个一个初始化,对于数组则需要遍历才能初始化,方法很呆!!!!!
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  int i = 0;
//  //初始化姓名
//  for(i=0;i<20;i++)
//  {//      (pc->data)->name[i] = '0';
//  }
//
//  //初始化性别
//  for (i = 0; i < 5; i++)
//  {//      (pc->data)->sex[i] = '0';
//  }
//
//  //初始化电话
//  for (i = 0; i < 12; i++)
//  {//      (pc->data)->tele[i] = 0;
//  }
//
//  //初始化年龄
//  (pc->data)->age = 0;
//
//  //初始化地址
//  for (i = 0; i < 30; i++)
//  {//      (pc->data)->addr[i] = 0;
//  }
//
//  //初始化sz
//  pc->sz = 0;
//}//静态版本
//方式2:初始化通讯录,使用memset
//void InitContact(struct Contact* pc) //结构体传参,这里不能使用结构体来接收,应该用结构体指针,因为对其内容进行修改,形参只是实参的一份临时拷贝,改变形参无法修改实参
//{//  assert(pc);
//  memset(pc->data, 0, sizeof(struct PeoInfo) * MAX);   //data是一个结构体,初始化可以采用memset
//  pc->sz = 0;
//}//加载通讯录
void LoadContact(struct Contact* pc)
{FILE* pfread = fopen("test.txt", "rb");if (pfread == NULL){perror("LoadContact:fopen");return;}//读数据//fread的返回值是size_t,返回成功读取的元素总数,失败则可能是发生了读取错误,不进入循环//二进制读struct PeoInfo temp = { 0 };while (fread(&temp, sizeof(struct PeoInfo), 1, pfread))    //使用fread需要用一个结构体来接收{//考虑增容check_capacity(pc);pc->data[pc->sz] = temp;   //将每次读取的数据放到指定位置上,方便查找和维护pc->sz++;}//关闭文件fclose(pfread);pfread = NULL;
}//动态版本
//初始化通讯录
void InitContact(struct Contact* pc)
{assert(pc);pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));if (pc->data == NULL){perror("InitContact");return;}else{//使用pc->sz = 0;pc->capacity = DEFAULT_SZ;}LoadContact(pc);
}//销毁通讯录-EXIT
void DestoryContact(struct Contact* pc)
{free(pc->data);pc->data = NULL;pc->sz = 0;pc->capacity = 0;
}//静态版本
//增加联系人
//void AddContact(struct Contact* pc)
//{//  assert(pc);  //断言,防止pc为空指针
//  if (pc->sz == MAX)
//  {//      printf("通讯录满了,无法添加数据\n");
//      return;
//  }
//  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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//  printf("请输入地址:>");
//  scanf("%s", pc->data[pc->sz].addr);
//
//  pc->sz++;
//  printf("成功添加联系人\n");
//}//检测容量够不够,不够了就扩容,返回1,够的话,也返回1,不需要扩容
//扩容失败,返回0
int check_capacity(struct Contact* pc)
{if (pc->sz == pc->capacity){//扩容//这里不能直接赋值给pc->data1,因为可能会增容失败struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (INC_SZ + pc->capacity) * sizeof(struct PeoInfo));if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;//printf("扩容成功\n");return 1;}else{perror("AddContact");return 0;}}elsereturn 1;  //不需要扩容
}//动态版本
//增加联系人
void AddContact(struct Contact* pc)
{assert(pc);  //断言,防止pc为空指针int ret = check_capacity(pc);if (ret == 0){return;  //扩容失败,直接返回,后面的就不执行了}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));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("成功添加联系人\n");}
}//展示通讯录
void ShowContact(const struct Contact* pc)
{assert(pc);int i = 0;printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零for (i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-12s\t%-2d\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 FindByName(const struct Contact* pc, char name[])  //因为不需要修改,所以采用const修饰
{assert(pc);//查找,要数组元素遍历int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;}}return -1;
}//删除联系人
void DelContact(struct Contact* pc)
{assert(pc);char name[MAX_NAME];   //存放输出删除的名字printf("请输入要删除的联系人姓名:>");scanf("%s", name);//不管是删除、查找、更改。都需要找输入的联系人是否存在,故将其封装成一个函数,存在返回位置,不存在返回-1//1:查找int ret = FindByName(pc, name);if (ret == -1)printf("要删除的联系人不存在\n");else{//删除int j = 0;for (j = ret; j < (pc->sz - 1); j++)   //100个数据的数组,下标最大为99{pc->data[j] = pc->data[j + 1];  //将返回值后面的数据整体往前覆盖一位。使用memmove也可以 }//memmove(&(pc->data[ret]), &(pc->data[ret + 1]), pc->sz - (ret + 1));printf("成功删除指定联系人\n");pc->sz--;}
}//查找联系人
void SearchContact(const struct Contact* pc)
{assert(pc);char name[MAX_NAME];printf("请输入要查找的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要查找的联系人不存在\n");else{//打印printf("%-20s\t%-5s\t%-12s\t%-2s\t%-30s\n", "姓名", "性别", "电话", "年龄", "地址");  //\t相当于tab,%-20s左对齐,多的后面补零printf("%-20s\t%-5s\t%-12s\t%-2d\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)
//{//  assert(pc);
//
//  char name[MAX_NAME];
//  printf("请输入要修改的联系人姓名:>");
//  scanf("%s", name);
//
//  //查找
//  int ret = FindByName(pc, name);
//  if (ret == -1)
//      printf("要修改的联系人不存在\n");
//  else
//  {//      printf("请输入名字:>");
//      scanf("%s", pc->data[ret].name);   //将ret位置的数据改了,重新输入一遍新的数据
//      printf("请输入性别:>");
//      scanf("%s", pc->data[ret].sex);
//      printf("请输入电话:>");
//      scanf("%s", pc->data[ret].tele);
//      printf("请输入年龄:>");
//      scanf("%d", &(pc->data[ret].age));  //由于其他的都是数组。直接写数组名就可以了,但是age是一个数据,需要取地址
//      printf("请输入地址:>");
//      scanf("%s", pc->data[ret].addr);
//
//      printf("修改联系人成功\n");
//  }
//}/***************************************************************************************************************************/
//可以指定修改信息!!!
//人物信息菜单
static void PeoMenu()
{printf("*************************************\n");printf("******   1.姓名      2.性别     ******\n");printf("******   3.电话      4.年龄     ******\n");printf("******   5.地址      0.退出    ******\n");printf("*************************************\n");
}void ModifyContact(struct Contact* pc)
{assert(pc);int input1;   //需要修改的数据char name[MAX_NAME];printf("请输入要修改的联系人姓名:>");scanf("%s", name);//查找int ret = FindByName(pc, name);if (ret == -1)printf("要修改的联系人不存在\n");else{do{PeoMenu();printf("请选择:>");scanf("%d", &input1);switch (input1){case 1:printf("请输入名字:>");scanf("%s", pc->data[ret].name);break;case 2:printf("请输入性别:>");scanf("%s", pc->data[ret].sex);break;case 3:printf("请输入电话:>");scanf("%s", pc->data[ret].tele);break;case 4:printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));break;case 5:printf("请输入地址:>");scanf("%s", pc->data[ret].addr);break;case 0:printf("修改完成,退出修改\n");break;default:printf("输入信息有误,请重新输入\n");break;}} while (input1);printf("修改联系人成功\n");}
}
/***************************************************************************************************************************///按照年龄排序
static int CmpByAge(const void* e1, const void* e2)
{return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}//排序指定方式,可以排序任意字符串类型,修改宏即可
static int CmpAnyWay(const void* e1, const void* e2)
{return strcmp(((struct PeoInfo*)e1)->SortWay, ((struct PeoInfo*)e2)->SortWay);
}//排序通讯录
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);     //按照年龄排序qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAnyWay);   //按照名字排序ShowContact(pc);printf("按照名字排序成功\n");
}//保存通讯录
void SaveContact(struct Contact* pc)
{FILE* pfwrite = fopen("test.txt", "wb");if (pfwrite == NULL){perror("SaveContact:fopen");return;}//写数据int i = 0;for (i = 0; i < pc->sz; i++){//二进制写fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pfwrite);   //每次写入一个结构体数据}//关闭文件fclose(pfwrite);pfwrite = NULL;}

以上就是文件通讯录的全部内容了

C语言小项目-《通讯录》(超详细讲解)相关推荐

  1. C语言小项目——通讯录的存储系统(静态版,动态版,文件版)

    目录 前言 一.总体设计框架 二.三种通讯录的功能阐述 三.静态通讯录 1.结构体设计 2.初始化通讯录 3.增加联系人的信息 4.删除联系人的信息 5.查找指定联系人并打印 6.修改联系人的信息 7 ...

  2. c语言小项目---通讯录2.0

    自从上次通讯录项目被字符串项目整的自闭了之后,用了5天时间重新整理了一下通讯录的思路,并且能够正常的使用,今天按模块把基于链表的通讯录2.0版本记录一下,供后续积累经验. 首先总结一下 通讯录2.0版 ...

  3. C语言实现扫雷游戏(超详细讲解+全部源码)

    电子信息 工科男 一点一点努力! 文章目录 前言 一.游戏介绍 二.游戏设计思路 二.具体步骤 1.创建test.c和game.c源文件以及 game.h头文件 2.创建菜单 3.创建雷盘 4.初始化 ...

  4. 学习笔记14-C语言-小项目-通讯录

    通讯录: 要求: 姓名,性别.电话,最多储存50个联系人 功能:1.添加联系人2.按名字删除联系人3.按姓名修改联系人信息4.查找联系人,可通过电话,名字查找,支持模糊查找5.显示所有联系人信息 代码 ...

  5. C语言小项目 -- 通讯录(静态版+动态版+文件版)

    文章目录 一.总体设计思路 1.设计背景 2.设计框架 3.功能概述 二.通讯录(静态版) 1.结构体设计 2.初始化通讯录 3.添加联系人信息 4.删除联系人信息 5.查找联系人(按姓名) 6.查找 ...

  6. C语言实现扫雷【超详细讲解】

    目录 一.实现扫雷的基本思路 二.代码实现的具体步骤 三.完整代码 1.saolei.h部分 2.saolei.c部分 3.test.c部分 扫雷和三子棋有很多相似的地方,相信大家认真学习完三子棋再将 ...

  7. Python新手爬虫训练小项目《爬取彼岸图网》(超详细讲解版)

    Python新手爬虫训练小项目<爬取彼岸图网>(超详细讲解版) 这是我的第一篇文章,作为一名新手爬虫,这个算是我这几天来的努力成果,虽然代码寥寥几行但花费了大半天,新手上路还是不能只看视频 ...

  8. C语言小项目之“究极无敌螺旋丸极爆炸狂拽炫酷五彩棒的”通讯录之*派小猩*作品

    C语言小项目之"究极无敌螺旋丸极爆炸狂拽炫酷五彩棒的"通讯录之派小猩作品 项目概述 基于C语言开发一个多功能的通讯录 功能目录 1.增加 2.删除 3.查找 4.修改 5.显示 6 ...

  9. react的超详细讲解

    create-react-app 项目目录 在HTML中使用react 1 2 3基础 React的注意事项 模拟的React 和 render React组件 函数组件 类组件 React 的数据源 ...

  10. python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上

    Python高级编程--装饰器Decorator超详细讲解(上篇) 送你小心心记得关注我哦!! 进入正文 全文摘要 装饰器decorator,是python语言的重要特性,我们平时都会遇到,无论是面向 ...

最新文章

  1. 分享个网盘,个人觉得很不错!
  2. [翻译]LightRacer游戏架构
  3. JAVA的JDBC连接数据库以及读取数据库数据
  4. WMS(二):Window的删除过程
  5. [.NET] Rough Dependency Injection
  6. 重温C# clr 笔记总结
  7. C语言指针与函数传参
  8. redis 内存不足 排查_排查redis占用内存达90%以上
  9. 单硬盘上mac + win7双系统,GUID-GPT分区
  10. Clumsy网络模拟工具
  11. webdriver 调用IE 问题
  12. CC2540开发板学习笔记(五)——串口通信
  13. PP点点通介绍与下载
  14. 【矩阵论】对称矩阵特征值的性质与直积
  15. 台达plc与串口软件通讯测试,台达PLC串口通信
  16. JS实现俄罗斯方块小游戏
  17. spark编程ERROR01——java.lang.NullPointerException
  18. dedeCMS自定义dede标签
  19. 佳沛奇异果猕猴桃扫盲
  20. 牛客OJ SQL后30题的解答

热门文章

  1. 关于LIS(普通方式及二分查找方式)
  2. 小程序 之 Promise
  3. Esxi重置root密码
  4. java语言程序设计培训_JAVA语言程序设计Z
  5. 一个MM的三国志10游戏经历
  6. android 主流机型适配,Android 10将适配三年前机型,再度证明了旗舰的价值
  7. 15-字符串字面量类型
  8. GoF设计模式之代理设计模式
  9. 正向(逆向)最大匹配和最大概率法分词的错误分析
  10. Android 10深色主题适配踩坑记录