c语言:通讯录管理系统(动态分配内存版)

简介: c语言:通讯录管理系统(动态分配内存版)

一.基础静态版本 (改进前)

这里我们给出基础版本的代码,然后本文后续内容都是基于此进行改进

我们分为 3 个文件来设计:

  • Contact.h:包含头文件的声明,对函数的声明,以及宏的申明
  • Contact.cpp:通讯录管理系统中具体每一个函数的实现
  • test.cpp: 主函数,根据用户的选择进行调用相应的函数

头文件部分(Contact.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<string.h>
#define Name_Max 20
#define Tel_Number 12
#define Sex_Max 5
#define Address_Max 30
#define Contact_Max 100
//联系人结构体
typedef struct PeopleInformation
{
  char name[Name_Max];
  char telnumber[Tel_Number];
  int age;
  char sex[Sex_Max];
  char address[Address_Max];
}PeoInfor;
//通讯录结构体
typedef struct Contact
{
  PeoInfor data[Contact_Max];//结构体数组存放联系人结构体
  int size;//记录当前通讯录中有多少个联系人
}Contact;
//目录
void menu();
//初始化通讯录
void InitContact(Contact* cp);
//增加联系人
void AddContact(Contact* cp);
//删除联系人
void DelContact(Contact* cp);
//通过姓名进行查找联系人
int FindPeople(Contact* cp, char name[]);
//展示全部通讯录信息
void ShowContact(const Contact* cp);
//查询联系人
void SeachPeople(Contact* cp);
//修改联系人信息
void ModifyContact(Contact* cp);

函数实现部分(Contact.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void menu()
{
  printf("\n");
  printf("-----------------------------\n");
  printf("---   1.添加联系人      -----\n");
  printf("---   2.删除联系人      -----\n");
  printf("---   3.查找联系人      -----\n");
  printf("---   4.修改联系人信息  -----\n");
  printf("---   5.显示全部信息    -----\n");
  printf("---   0.退出通讯录      -----\n");
  printf("-----------------------------\n");
}
//初始化通讯录
void InitContact(Contact* cp)
{
  //判断非空
  assert(cp);
  cp->size = 0;
  memset(cp->data, 0, sizeof(cp->data));
}
//增加联系人
void AddContact(Contact* cp)
{
  //判断非空
  assert(cp);
  //判断未满
  if (cp->size == Contact_Max)
  {
    printf("通讯录已满,无法再添加新的联系人\n");
    return;
  }
  printf("请输入要添加的联系人的姓名:\n");
  scanf("%s", cp->data[cp->size].name);
  printf("请输入要添加的联系人的电话号:\n");
  scanf("%s", cp->data[cp->size].telnumber);
  printf("请输入要添加的联系人的年龄:\n");
  scanf("%d", &(cp->data[cp->size].age));
  printf("请输入要添加的联系人的性别:\n");
  scanf("%s", cp->data[cp->size].sex);
  printf("请输入要添加的联系人的住址:\n");
  scanf("%s", cp->data[cp->size].address);
  cp->size++;
  printf("添加成功\n");
}
//通过姓名进行查找联系人
int FindPeople(Contact* cp, char name[])
{
  assert(cp);
  for (int i = 0; i < cp->size; i++)
  {
    if (strcmp(cp->data[i].name, name) == 0)
    {
      return i;
    }
  }
  return -1;
}
//删除联系人
void DelContact(Contact* cp)
{
  assert(cp);
  char name[Name_Max];
  if (cp->size == 0)
  {
    printf("通讯录为空,无需删除\n");
    return;
  }
  printf("请输入选择删除的联系人的姓名:\n");
  scanf("%s", name);
  int ret = FindPeople(cp, name);
  if (ret == -1)
  {
    printf("要删除的联系人不存在\n");
    return;
  }
  for (int i = ret; i < cp->size-1 ; i++)
  {
    cp->data[i] = cp->data[i + 1];
  }
  cp->size--;
  printf("删除成功\n");
}
//查询联系人
void SeachPeople(Contact* cp)
{
  assert(cp);
  char name[Name_Max];
  if (cp->size == 0)
  {
    printf("通讯录为空\n");
    return;
  }
  printf("请输入选择查找的联系人的姓名:\n");
  scanf("%s", name);
  int ret = FindPeople(cp, name);
  if (ret == -1)
  {
    printf("要查找的联系人不存在\n");
    return;
  }
  //名字  年龄  性别    电话    地址
  //xxx   xxx    xxx    xxx     xxx
  printf("%-10s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
  //打印个人的信息
  printf("%-10s%-5d%-5s%-12s%-30s\n", cp->data[ret].name, cp->data[ret].age, cp->data[ret].sex, cp->data[ret].telnumber, cp->data[ret].address);
}
//展示全部通讯录信息
void ShowContact(const Contact* cp)
{
  assert(cp);
  if (cp->size == 0)
  {
    printf("通讯录为空\n");
    return;
  }
  //名字  年龄  性别    电话    地址
  //xxx   xxx    xxx    xxx     xxx
  printf("%-10s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
  for (int i = 0; i < cp->size; i++)
  {
    //打印每个人的信息
    printf("%-10s%-5d%-5s%-12s%-30s\n",cp->data[i].name, cp->data[i].age, cp->data[i].sex, cp->data[i].telnumber, cp->data[i].address);
  }
}
//修改联系人信息
void ModifyContact(Contact* cp)
{
  assert(cp);
  char name[Name_Max];
  if (cp->size == 0)
  {
    printf("通讯录为空\n");
    return;
  }
  printf("请输入选择修改的联系人的姓名:\n");
  scanf("%s", name);
  int ret = FindPeople(cp, name);
  if (ret == -1)
  {
    printf("要修改的联系人信息不存在\n");
    return;
  }
  printf("请输入要修改的联系人的姓名:\n");
  scanf("%s", cp->data[ret].name);
  printf("请输入要修改的联系人的电话号:\n");
  scanf("%s", cp->data[ret].telnumber);
  printf("请输入要修改的联系人的年龄:\n");
  scanf("%d", &(cp->data[ret].age));
  printf("请输入要修改的联系人的性别:\n");
  scanf("%s", cp->data[ret].sex);
  printf("请输入要修改的联系人的住址:\n");
  scanf("%s", cp->data[ret].address);
  printf("修改成功\n");
}

主函数部分(test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
//枚举,增加程序的可读性
enum options
{
  EXIT,
  ADD,
  DEL,
  SEACH,
  MODIFY,
  SHOW
};
int main()
{
  int input = 0;
  //创建通讯录
  Contact con;
  //初始化通讯录
  InitContact(&con);
  do
  {
    menu();
    printf("请输入你的选择: ");
    scanf("%d", &input);
    switch (input)
    {
    //增加联系人信息
    case ADD:
      AddContact(&con);
      break;
    //删除联系人信息
    case DEL:
      DelContact(&con);
      break;
    //查找某个联系人的信息
    case SEACH:
      SeachPeople(&con);
      break;
    //修改某个联系人的信息
    case MODIFY:
      ModifyContact(&con);
      break;
    //展示通讯录内的每一个联系人的信息
    case SHOW:
      ShowContact(&con);
      break;
    //退出通讯录管理系统
    case EXIT:
      printf("通讯录已退出\n");
      break;
    //预防非法输入
    default:
      printf("输入错误,请重新输入\n");
      break;
    }
  }while(input);
  return 0;
}

二.结构体的更改

       动态的分配内存就意味着通讯录这个结构体要动态的分配内存,根据通讯录内的信息进行分配,所以我们在这里对于通讯录结构体进行更改

//通讯录结构体
typedef struct Contact
{
  PeoInfor* data;//结构体数组存放联系人结构体
  int size;//记录当前通讯录中有多少个联系人
  int capacity;//记录当前存放的容量
}Contact;

      在这里我们将 data 从一个结构体数组改成了结构体指针,然后后续再使用 这个指针指向我们动态开辟的内存就完成了我们的需求设计

       并且新增了个变量 capacity 用来记录当前通讯录内的最大容量,当联系人的数量和容量相同的时候,也就是通讯录满容的时候,我们再使用 realloc 重新分配新的内存空间

三.扩容的设计

       我们封装一个函数方便我们添加新的联系人的时候进行扩容,先判断当前通讯录是否已满,如果满了就进行扩容,每一次扩容扩展 2 个联系人结构体的大小

首先是判断部分,当当前通讯录的容量等于通讯录内实际存放的数据的大小的时候,我们就判定为通讯录已满,然后我们使用 realloc 开辟新的空间,比之前大 2 个联系人结构体的大小

为了程序的健全性,我们也要判断开辟空间是否成功,如果成功就通过 ptr指针 指向联系人的数据部分,用 data 接收,如果开辟失败,我们就打印报错信息

void CheckContact(Contact* cp)
{
  if (cp->size == cp->capacity)
  {
    PeoInfor* ptr = (PeoInfor*)realloc(cp->data, (cp->capacity + 2) * sizeof(PeoInfor));
    if (ptr != NULL)
    {
      cp->data = ptr;
      cp->capacity += 2;
      printf("增容成功\n");
    }
    else
    {
      perror("AddContact->realloc");
      return;
    }
  }
}

四.释放空间

       由程序员申请开辟的空间也应当由程序员设置进行释放,在这个通讯录管理系统中也是如此,我们需要找到合适的释放位置,也就是当用户退出通讯录的时候,我们手动进行对开辟的空间进行释放,以避免造成内存泄漏

       那我们这里就封装一个释放空间的函数:

//销毁通讯录
void DestoryContact(Contact* cp)
{
  free(cp->data);
  cp->data = NULL;
  cp->size = 0;
  cp->capacity = 0;
}

五.最终完整代码 (改进后)

头文件部分(Contact.h:

#pragma once
#pragma once
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#define Name_Max 20
#define Tel_Number 12
#define Sex_Max 5
#define Address_Max 30
#define Contact_Max 100
#define Contact_SZ 3
//联系人结构体
typedef struct PeopleInformation
{
  char name[Name_Max];
  char telnumber[Tel_Number];
  int age;
  char sex[Sex_Max];
  char address[Address_Max];
}PeoInfor;
//通讯录结构体
typedef struct Contact
{
  PeoInfor* data;//结构体数组存放联系人结构体
  int size;//记录当前通讯录中有多少个联系人
  int capacity;//记录当前存放的容量
}Contact;
//目录
void menu();
//初始化通讯录
void InitContact(Contact* cp);
//增加联系人
void AddContact(Contact* cp);
//删除联系人
void DelContact(Contact* cp);
//通过姓名进行查找联系人
int FindPeople(Contact* cp, char name[]);
//展示全部通讯录信息
void ShowContact(const Contact* cp);
//查询联系人
void SeachPeople(Contact* cp);
//修改联系人信息
void ModifyContact(Contact* cp);
//扩容
void CheckContact(Contact* cp);
//销毁通讯录
void DestoryContact(Contact* cp);

函数的实现部分 (Contact.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void menu()
{
  printf("\n");
  printf("-----------------------------\n");
  printf("---   1.添加联系人      -----\n");
  printf("---   2.删除联系人      -----\n");
  printf("---   3.查找联系人      -----\n");
  printf("---   4.修改联系人信息  -----\n");
  printf("---   5.显示全部信息    -----\n");
  printf("---   0.退出通讯录      -----\n");
  printf("-----------------------------\n");
}
//初始化通讯录
void InitContact(Contact* cp)
{
  //判断非空
  assert(cp);
  cp->size = 0;
  cp->capacity = Contact_SZ;
  cp->data =(PeoInfor*)calloc(cp->capacity, sizeof(PeoInfor));
  if (cp->data == NULL)
  {
    perror("InitContact->calloc");
    return;
  }
}
void CheckContact(Contact* cp)
{
  if (cp->size == cp->capacity)
  {
    PeoInfor* ptr = (PeoInfor*)realloc(cp->data, (cp->capacity + 2) * sizeof(PeoInfor));
    if (ptr != NULL)
    {
      cp->data = ptr;
      cp->capacity += 2;
      printf("增容成功\n");
    }
    else
    {
      perror("AddContact->realloc");
      return;
    }
  }
}
//增加联系人
void AddContact(Contact* cp)
{
  //判断非空
  assert(cp);
  //判断满后扩容
  CheckContact(cp);
  printf("请输入要添加的联系人的姓名:\n");
  scanf("%s", cp->data[cp->size].name);
  printf("请输入要添加的联系人的电话号:\n");
  scanf("%s", cp->data[cp->size].telnumber);
  printf("请输入要添加的联系人的年龄:\n");
  scanf("%d", &(cp->data[cp->size].age));
  printf("请输入要添加的联系人的性别:\n");
  scanf("%s", cp->data[cp->size].sex);
  printf("请输入要添加的联系人的住址:\n");
  scanf("%s", cp->data[cp->size].address);
  cp->size++;
  printf("添加成功\n");
}
//通过姓名进行查找联系人
int FindPeople(Contact* cp, char name[])
{
  assert(cp);
  for (int i = 0; i < cp->size; i++)
  {
    if (strcmp(cp->data[i].name, name) == 0)
    {
      return i;
    }
  }
  return -1;
}
//删除联系人
void DelContact(Contact* cp)
{
  assert(cp);
  char name[Name_Max];
  if (cp->size == 0)
  {
    printf("通讯录为空,无需删除\n");
    return;
  }
  printf("请输入选择删除的联系人的姓名:\n");
  scanf("%s", name);
  int ret = FindPeople(cp, name);
  if (ret == -1)
  {
    printf("要删除的联系人不存在\n");
    return;
  }
  for (int i = ret; i < cp->size - 1; i++)
  {
    cp->data[i] = cp->data[i + 1];
  }
  cp->size--;
  printf("删除成功\n");
}
//查询联系人
void SeachPeople(Contact* cp)
{
  assert(cp);
  char name[Name_Max];
  if (cp->size == 0)
  {
    printf("通讯录为空\n");
    return;
  }
  printf("请输入选择查找的联系人的姓名:\n");
  scanf("%s", name);
  int ret = FindPeople(cp, name);
  if (ret == -1)
  {
    printf("要查找的联系人不存在\n");
    return;
  }
  //名字  年龄  性别    电话    地址
  //xxx   xxx    xxx    xxx     xxx
  printf("%-10s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
  //打印个人的信息
  printf("%-10s%-5d%-5s%-12s%-30s\n", cp->data[ret].name, cp->data[ret].age, cp->data[ret].sex, cp->data[ret].telnumber, cp->data[ret].address);
}
//展示全部通讯录信息
void ShowContact(const Contact* cp)
{
  assert(cp);
  if (cp->size == 0)
  {
    printf("通讯录为空\n");
    return;
  }
  //名字  年龄  性别    电话    地址
  //xxx   xxx    xxx    xxx     xxx
  printf("%-10s%-5s%-5s%-12s%-30s\n", "名字", "年龄", "性别", "电话", "地址");
  for (int i = 0; i < cp->size; i++)
  {
    //打印每个人的信息
    printf("%-10s%-5d%-5s%-12s%-30s\n", cp->data[i].name, cp->data[i].age, cp->data[i].sex, cp->data[i].telnumber, cp->data[i].address);
  }
}
//修改联系人信息
void ModifyContact(Contact* cp)
{
  assert(cp);
  char name[Name_Max];
  if (cp->size == 0)
  {
    printf("通讯录为空\n");
    return;
  }
  printf("请输入选择修改的联系人的姓名:\n");
  scanf("%s", name);
  int ret = FindPeople(cp, name);
  if (ret == -1)
  {
    printf("要修改的联系人信息不存在\n");
    return;
  }
  printf("请输入要修改的联系人的姓名:\n");
  scanf("%s", cp->data[ret].name);
  printf("请输入要修改的联系人的电话号:\n");
  scanf("%s", cp->data[ret].telnumber);
  printf("请输入要修改的联系人的年龄:\n");
  scanf("%d", &(cp->data[ret].age));
  printf("请输入要修改的联系人的性别:\n");
  scanf("%s", cp->data[ret].sex);
  printf("请输入要修改的联系人的住址:\n");
  scanf("%s", cp->data[ret].address);
  printf("修改成功\n");
}
//销毁通讯录
void DestoryContact(Contact* cp)
{
  free(cp->data);
  cp->data = NULL;
  cp->size = 0;
  cp->capacity = 0;
}

主函数部分(test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
//枚举,增加程序的可读性
enum options
{
  EXIT,
  ADD,
  DEL,
  SEACH,
  MODIFY,
  SHOW
};
int main()
{
  int input = 0;
  //创建通讯录
  Contact con;
  //初始化通讯录
  InitContact(&con);
  do
  {
    menu();
    printf("请输入你的选择: ");
    scanf("%d", &input);
    switch (input)
    {
      //增加联系人信息
    case ADD:
      AddContact(&con);
      break;
      //删除联系人信息
    case DEL:
      DelContact(&con);
      break;
      //查找某个联系人的信息
    case SEACH:
      SeachPeople(&con);
      break;
      //修改某个联系人的信息
    case MODIFY:
      ModifyContact(&con);
      break;
      //展示通讯录内的每一个联系人的信息
    case SHOW:
      ShowContact(&con);
      break;
      //退出通讯录管理系统
    case EXIT:
      DestoryContact(&con);
      printf("通讯录已退出\n");
      break;
      //预防非法输入
    default:
      printf("输入错误,请重新输入\n");
      break;
    }
  } while (input);
  return 0;
}



本次分享就到此为止了,感谢您的支持,如有错误,欢迎积极指正

目录
相关文章
|
19天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
30 3
|
16天前
|
存储 缓存 监控
|
19天前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
101 2
|
4天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
13 3
|
20天前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
22天前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
18 2
|
25天前
|
存储 程序员 编译器
C语言——动态内存管理与内存操作函数
C语言——动态内存管理与内存操作函数
|
6天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
9 0
|
12天前
|
运维 JavaScript Linux
容器内的Nodejs应用如何获取宿主机的基础信息-系统、内存、cpu、启动时间,以及一个df -h的坑
本文介绍了如何在Docker容器内的Node.js应用中获取宿主机的基础信息,包括系统信息、内存使用情况、磁盘空间和启动时间等。核心思路是将宿主机的根目录挂载到容器,但需注意权限和安全问题。文章还提到了使用`df -P`替代`df -h`以获得一致性输出,避免解析错误。
|
15天前
|
C语言
保姆级教学 - C语言 之 动态内存管理
保姆级教学 - C语言 之 动态内存管理
13 0