C语言之通讯录的实现篇优化版

简介: C语言之通讯录的实现篇优化版

我们前面已经学习完 动态内存管理 和 C语言操作文件,相信你对内存的管理和操作有了更加深入的了解。那我们今天接着来优化一下前面我们写过的通讯录。

  • 特别提醒:函数如果放在后面,在使用函数前面必须声明!!

动态内存管理

使用动态内存相关的知识优化通讯录。主要是从增加内存的方面去优化。我们设置为以下规则,注意这种设置仅仅为了方便讲解和测试仅此而已。后期大家可以设置为自己想要的规则。

  • 通讯录刚刚开始可以存放3个人的信息。
  • 通讯录放满,可以增加容量,每次增加2个人的信息的空间。

通讯录声明

静态版本

//通讯录---存放结构体的数组--每个结构体就是一个人的信息
typedef struct Contact
{
  PeoInfo data[MAX];//存放数据
  int sz;//记录当前通讯录中存放的人的信息个数
}Contact;

动态版本

//动态版本
typedef struct Contact
{
  PeoInfo* data;//存放数据,可修改数组内存的空间了
//🆗PeoInfo结构体类型的指针
  int sz;//记录当前通讯录中存放的人的信息个数
  int capacity;//记录的是通讯录的当前容量
}Contact;
  • 当通讯录中存放的人的信息个数 sz == 通讯录当前容量 capacity就可以考虑增加容量问题。
  • 目前的data是没有空间的,需要在初始化的时候在堆区开辟3个人的信息空间。
  • PeoInfo* data  存放数据,可修改数组内存的空间了,🆗PeoInfo结构体类型的指针
  • PeoInfo* data  指针维护开辟的空间


初始化通讯录

静态版本

void InitContact(Contact* pc)
{
  assert(pc);
  pc->sz = 0;
  memset(pc->data, 0, sizeof(pc->data));
}

动态版本

#define DEFAULT_SZ 3 //后期容易修改
void InitContact(Contact* pc)
{
  assert(pc);
  pc->sz = 0;
  pc->capacity = DEFAULT_SZ;
  pc->data = calloc(pc->capacity ,sizeof(PeoInfo));
  if (pc->data == NULL)
  {
    perror("InitContact->calloc");
    return;
  }
}
  • PeoInfo* data  指针维护开辟的空间

Add增加通讯录

静态版本

void AddContact(Contact* pc)
{
  assert(pc);
  printf("请输入名字\n");
  scanf("%s", pc->data[pc->sz].name);//name是数组名,不用&
  printf("请输入年龄\n");
  scanf("%d", &(pc->data[pc->sz].age));//
  printf("请输入性别\n");
  scanf("%s", pc->data[pc->sz].sex);
  printf("请输入电话\n");
  scanf("%s", pc->data[pc->sz].tele);
  printf("请输入地址\n");
  scanf("%s", pc->data[pc->sz].addr);
  //
  pc->sz++;
  printf("增加成功\n");
}

动态版本

#define DEFAULT_INC 2//后期容易修改
//动态版本
void AddContact(Contact* pc)
{
  assert(pc);
  //满了增加容量
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));
    if (ptr != NULL)
    {
      pc->data = ptr;
    }
    else
    {
      perror("AddContact->realloc");
      return;
    }
  }
  //不需要增容填写人信息
  printf("请输入名字\n");
  scanf("%s", pc->data[pc->sz].name);//name是数组名,不用&
  printf("请输入年龄\n");
  scanf("%d", &(pc->data[pc->sz].age));//
  printf("请输入性别\n");
  scanf("%s", pc->data[pc->sz].sex);
  printf("请输入电话\n");
  scanf("%s", pc->data[pc->sz].tele);
  printf("请输入地址\n");
  scanf("%s", pc->data[pc->sz].addr);
  //
  pc->sz++;
  printf("增加成功\n");
}
  • 当通讯录中存放的人的信息个数 sz == 通讯录当前容量 capacity就可以考虑增加容量问题。

当然也可以封装成函数Checkcapacity

Checkcapacity增容

Checkcapacity(pc);
void Checkcapacity(Contact* pc)
{
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(PeoInfo));
    if (ptr != NULL)
    {
      pc->data = ptr;
    }
    else
    {
      perror("AddContact->realloc");
      return;
    }
  }
}

当然我们使用了动态内存的空间,我们必须手动释放和销毁,我们封装一个函数去销毁。

DestroyContact释放动态空间

void DestroyContact(Contact* pc);
case EXIT:
  DestroyContact(&con);
  printf("退出通讯录\n");
  break;
void DestroyContact(Contact* pc)
{
  free(pc->data);
  pc->data = NULL;
  pc->sz = 0;
  pc->capacity = 0;
}

文件操作

程序退出之后,输入的信息都丢了。所以在通讯录之前最好把我们输入的信息全部保存到文件里。

SaveContact保存信息到文件中

//保存信息到文件
void SaveContact(Contact* pc);
case EXIT:
  //保存通讯录中的数据到文件中
  SaveContact(&con);
  DestroyContact(&con);
  printf("退出通讯录\n");
  break;
void SaveContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "wb");//wb以二进制的方式写数据到文件
  if (pf == NULL)
  {
    perror("SaveContact");
    return;
  }
  //写信息到文件
  int i = 0;//一个数据一个数据写
  for (i = 0; i < pc->sz; i++)
  {
    fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
    fwrite(pc->data+i, sizeof(PeoInfo), 1, pf);
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
}
  • 字符以ASCII码和二进制的存储的方式相同
  • 整型以ASCII码和二进制的存储的方式不同

初始化通讯录

但是当程序再次跑起来的时候,还是看不到信息??所以我们需要在执行通讯录等一系列的功能之前我们就要从文件里读取到数据信息。我们去修改初始化部分


旧版本

void InitContact(Contact* pc)
{
  assert(pc);
  pc->sz = 0;
  pc->capacity = DEFAULT_SZ;
  pc->data = calloc(pc->capacity * sizeof(PeoInfo));
  if (pc->data == NULL)
  {
    perror("InitContact->calloc");
    return;
  }
}

文件版本

//文件版本
void InitContact(Contact* pc)
{
  assert(pc);
  pc->sz = 0;
  pc->capacity = DEFAULT_SZ;
  pc->data = calloc(pc->capacity * sizeof(PeoInfo));
  if (pc->data == NULL)
  {
    perror("InitContact->calloc");
    return;
  }
  LoadContact(pc);
}

LoadContact加载文件信息到通讯录

//加载文件信息到通讯录
void LoadContact(Contact* pc);
void Checkcapacity(Contact* pc);//声明
void LoadContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "rb");//以二进制的形式读
  if (pf == NULL)
  {
    perror("LoadContact");
    return;
  }
  //读文件
  PeoInfo tmp = { 0 };//创建临时变量,结构体PeoInfo类型
  while (fread(&tmp, sizeof(PeoInfo), 1, pf))//读到就返回1,没有读到就返回0,一个一个读直到没有读到为0跳出循环
  {
    Checkcapacity(pc);//若文件中有5个人,但是这里只有3个人的容量,首先判断需不需要增容
    pc->data[pc->sz] = tmp;
    pc->sz++;
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
  printf("************************************\n");
  printf("********1.add      2.del************\n");
  printf("********3.search   4.modify*********\n");
  printf("********5.show     6.sort***********\n");
  printf("********0.exit           ***********\n");
  printf("************************************\n");
}
enum Option
{
  EXIT,//0
  ADD,//1
  DEL,//2
  SEARCH,//3
  MODIFY,//4
  SHOW,//5
  SORT,//6  
};
int main()
{
  int input = 0;
  //创建通讯录
  Contact con;
  //初始化通讯录
  InitContact(&con);
  do
  {
    menu();
    printf("请输入你的选择:>\n");
    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:
      //保存通讯录中的数据到文件中
      SaveContact(&con);
      DestroyContact(&con);
      printf("退出通讯录\n");
      break;
    default:
      printf("选择错误,请重新选择:>\n");
      break;
    }
  } while (input);
  return 0;
}

contact.h

#pragma once
#include<stdio.h>
#include<string.h>
#include<assert.h>
#define NAME_MAX 20
#define SEX_MAX 20
#define TELE_MAX 20
#define ADDR_MAX 30
#define MAX 100
#define DEFAULT_SZ 3
#define DEFAULT_INC 2
typedef struct PeoInfo
{
  char name[NAME_MAX];
  int age;
  char sex[SEX_MAX];
  char tele[TELE_MAX];
  char addr[ADDR_MAX];
}PeoInfo;
//静态版本
//typedef struct Contact
//{
//  PeoInfo data[MAX];//存放数据
//  int sz;//记录当前通讯录中存放的人的信息个数
//}Contact;
//动态版本
typedef struct Contact
{
  PeoInfo* data;//存放数据
  int sz;//记录当前通讯录中存放的人的信息个数
  int capacity;//记录的是通讯录的当前容量
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加个人信息
void AddContact(Contact* pc);
//展示个人信息
void ShowContact(Contact* pc);
//删除个人信息
void DelContact(Contact *pc);
//查找个人信息
void SearchContact(Contact* pc);
//修改个人信息
void ModifyContact(Contact* pc);
//排序
//void SortContact(Contact* pc);
//释放空间
void DestroyContact(Contact* pc);
//保存信息到文件
void SaveContact(Contact* pc);
//加载文件信息到通讯录
void LoadContact(Contact* pc);

contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//初始化静态版本
//void InitContact(Contact* pc)
//{
//  assert(pc);
//  pc->sz = 0;
//  memset(pc->data, 0, sizeof(pc->data));
//}
//动态版本
//void InitContact(Contact* pc)
//{
//  assert(pc);
//  pc->sz = 0;
//  pc->capacity = DEFAULT_SZ;
//  pc->data = calloc(pc->capacity * sizeof(PeoInfo));
//  if (pc->data == NULL)
//  {
//    perror("InitContact->calloc");
//    return;
//  }
//}
//文件版本
void Checkcapacity(Contact* pc);//声明
void LoadContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "rb");//以二进制的形式读
  if (pf == NULL)
  {
    perror("LoadContact");
    return;
  }
  //读文件
  PeoInfo tmp = { 0 };
  while (fread(&tmp, sizeof(PeoInfo), 1, pf))
  {
    Checkcapacity(pc);
    pc->data[pc->sz] = tmp;
    pc->sz++;
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
}
void InitContact(Contact* pc)
{
  assert(pc);
  pc->sz = 0;
  pc->capacity = DEFAULT_SZ;
  pc->data = calloc(pc->capacity * sizeof(PeoInfo));
  if (pc->data == NULL)
  {
    perror("InitContact->calloc");
    return;
  }
  LoadContact(pc);
}
//增加静态版本
//void AddContact(Contact* pc)
//{
//  assert(pc);
//  printf("请输入名字\n");
//  scanf("%s", pc->data[pc->sz].name);//name是数组名,不用&
//  printf("请输入年龄\n");
//  scanf("%d", &(pc->data[pc->sz].age));//
//  printf("请输入性别\n");
//  scanf("%s", pc->data[pc->sz].sex);
//  printf("请输入电话\n");
//  scanf("%s", pc->data[pc->sz].tele);
//  printf("请输入地址\n");
//  scanf("%s", pc->data[pc->sz].addr);
//  //
//  pc->sz++;
//  printf("增加成功\n");
//}
//动态版本
void Checkcapacity(Contact* pc)
{
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));
    if (ptr != NULL)
    {
      pc->data = ptr;
    }
    else
    {
      perror("AddContact->realloc");
      return;
    }
  }
}
void AddContact(Contact* pc)
{
  assert(pc);
  //满了增加容量
  Checkcapacity(pc);
  //不需要增容填写人信息
  printf("请输入名字\n");
  scanf("%s", pc->data[pc->sz].name);//name是数组名,不用&
  printf("请输入年龄\n");
  scanf("%d", &(pc->data[pc->sz].age));//
  printf("请输入性别\n");
  scanf("%s", pc->data[pc->sz].sex);
  printf("请输入电话\n");
  scanf("%s", pc->data[pc->sz].tele);
  printf("请输入地址\n");
  scanf("%s", pc->data[pc->sz].addr);
  //
  pc->sz++;
  printf("增加成功\n");
}
//展示
void ShowContact(const Contact* pc)
{
  assert(pc);
  if (pc->sz == 0)
  {
    printf("通讯录为空,无需打印\n");
    return 0;
  }
  int i = 0;
  //名字 年龄 性别 电话 地址
  //-左对齐
  //20是需要20字符的位置来放名字 
  printf("%-20s%-5s%-5s%-12s%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
  for (i = 0; i < pc->sz; i++)
  {
    printf("%-20s%-5d%-5s%-12s%-30s\n",
      pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
  }
}
//删除,查找,修改都需要使用
int FindByName(const Contact* pc, char name[])
{
  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(Contact* pc)
{
  char name[NAME_MAX];//
  assert(pc);
  if (pc->sz == 0)
  {
    printf("通讯录为空,无法删除\n");
    return;
  }
  printf("输入要删除的名字:>");
  scanf("%s", name);
  int ret = FindByName(pc, name);
  if (ret == -1)
  {
    printf("要删除的人不存在\n");
    return;
  }
  //存在返回这个人的所在data的下标放入ret
  int i = 0;
  for (i = ret; i < pc -> sz - 1; i++)
  {
    pc->data[i] = pc->data[i + 1];
  }
  pc->sz--;
  printf("删除成功\n");
}
//查找
void SearchContact(Contact* pc)
{
  char name[NAME_MAX];
  assert(pc);
  if (pc->sz == 0)
  {
    printf("通讯录为空,无法查找\n");
    return;
  }
  //查找
  printf("输入要查找的名字:>");
  scanf("%s", name);
  int ret = FindByName(pc, name);
  if (ret == -1)
  {
    printf("要查找的人不存在\n");
    return;
  }
  //返回下标显示这个的信息🆗
  printf("%-20s%-5s%-5s%-12s%-30s\n", "姓名", "年龄", "性别", "电话", "地址");
  printf("%-20s%-5d%-5s%-12s%-30s\n",
      pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}
//修改
void ModifyContact(Contact* pc)
{
  char name[NAME_MAX];
  assert(pc);
  if (pc->sz == 0)
  {
    printf("通讯录为空,无法修改\n");
    return;
  }
  //查找
  printf("输入要修改的名字:>");
  scanf("%s", name);
  int ret = FindByName(pc, name);
  if (ret == -1)
  {
    printf("要修改的人不存在\n");
    return;
  }
  //修改
  assert(pc);
  printf("请输入名字\n");
  scanf("%s", pc->data[ret].name);//name是数组名,不用&
  printf("请输入年龄\n");
  scanf("%d", &(pc->data[ret].age));//
  printf("请输入性别\n");
  scanf("%s", pc->data[ret].sex);
  printf("请输入电话\n");
  scanf("%s", pc->data[ret].tele);
  printf("请输入地址\n");
  scanf("%s", pc->data[ret].addr);
  printf("修改成功\n");
}
//释放动态空间
void DestroyContact(Contact* pc)
{
  free(pc->data);
  pc->data = NULL;
  pc->sz = 0;
  pc->capacity = 0;
}
//写函数到文件中
void SaveContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "wb");//wb以二进制的方式写数据到文件
  if (pf == NULL)
  {
    perror("SaveContact");
    return;
  }
  //写信息到文件
  int i = 0;//一个数据一个数据写
  for (i = 0; i < pc->sz; i++)
  {
    fwrite(&(pc->data[i]), sizeof(PeoInfo), 1, pf);
    fwrite(pc->data+i, sizeof(PeoInfo), 1, pf);
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
}

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!

接下来的博文会更新一些练习题,到实践中去加深对知识的理解。C语言的基本学习就快结束了,还是要多加练习。🆗

代码----------→【gitee:唐棣棣 (TSQXG) - Gitee.com

联系----------→ 【邮箱:2784139418@qq.com】

目录
相关文章
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
65 1
|
3月前
|
算法 搜索推荐 C语言
【C语言】冒泡排序+优化版
【C语言】冒泡排序+优化版
|
3月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
41 2
|
3月前
|
存储 C语言
手把手教你用C语言实现通讯录管理系统
手把手教你用C语言实现通讯录管理系统
|
5月前
|
存储 搜索推荐 算法
【C语言】C语言—通讯录管理系统(源码)【独一无二】
【C语言】C语言—通讯录管理系统(源码)【独一无二】
|
5月前
|
存储 数据可视化 C语言
【C语言】C语言 手机通讯录系统的设计 (源码+数据+论文)【独一无二】
【C语言】C语言 手机通讯录系统的设计 (源码+数据+论文)【独一无二】
|
7月前
|
机器学习/深度学习 搜索推荐 程序员
C语言实现个人通讯录(功能优化)-2
C语言实现个人通讯录(功能优化)
|
7月前
|
存储 C语言 索引
C语言实现个人通讯录(功能优化)-1
C语言实现个人通讯录(功能优化)
C语言实现个人通讯录(功能优化)-1
|
6月前
|
存储 编译器 定位技术
结构体数组在C语言中的应用与优化策略
结构体数组在C语言中的应用与优化策略
|
6月前
|
存储 编译器 数据库
结构体数组在C语言中的应用与优化技巧
结构体数组在C语言中的应用与优化技巧