【C语言】实现动态版通讯录

简介: 【C语言】实现动态版通讯录

前言:

前面我们学过了结构体、枚举等自定义类型的学习,写了一个静态版本的通讯录【传送门】点击进入静态版通讯录界面,我们知道,我们写的通讯录其实本质用的是一个结构体数组,数组的大小是固定的,这时我们就可以使用动态内存管理相关知识,对内存空间进行一个合理的分配,而不至于一下开辟一个较大的数组空间,用的时候却很少,造成了内存浪费的问题等。如果在内存不够时,也可以进行申请一定的空间来存储数据。


一、静态版本代码实现:

这里我就直接将完整代码放过来,具体分析,可以点击传送门链接,看实现思路哦~~


test.c文件代码(测试通讯录的相关功能)

#include "contact.h"
void Menu()
{
  printf("********************************\n");
  printf("******   0.exit 1.add     ******\n");
  printf("******  2.delete  3.modify******\n");
  printf("******  4.search 5.show    *****\n");
  printf("******      6.sort         *****\n");
  printf("********************************\n");
}
enum Option
{
  EXIT,
  ADD,
  DELETE,
  MODIFY,
  SEARCH,
  SHOW,
  SORT
};
int main()
{
  Contact con;//创建一个名为con的结构体变量
  InitContact(&con);
  int input = 0;
  do
  {
    Menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case ADD:
      AddContact(&con);
      break;
    case DELETE:
      DeleteContact(&con);
      break;
    case MODIFY:
      ModifyContact(&con);
      break;
    case SEARCH:
      SearchContact(&con);
      break;
    case SHOW:
      ShowContact(&con);
      break;
    case SORT:
      SortContact(&con);
      break;
    case EXIT:
      printf("退出通讯录\n");
      break;
    default:
      printf("输入有误,请重新输入\n");
      break;
    }
  } while (input);
}

contact.h文件(用来声明函数、结构体变量)

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 15
#define MAX_ADDR 20
#define MAX 20 
//创建个人信息
typedef struct PeoInfo
{
  char name[MAX_NAME];
  int age;
  char sex[MAX_SEX];
  char tele[MAX_TELE];
  char addr[MAX_ADDR];
}PeoInfo;
//静态版本的通讯录
//声明一个通讯录结构体
typedef struct Contact
{
  PeoInfo data[MAX];
  int sz;
}Contact;
//初始化结构体
void InitContact(Contact* pc);
//添加联系人
void AddContact(Contact* pc);
//显示通讯录
void ShowContact(const Contact* pc);
//删除指定联系人
void DeleteContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//排序通讯录
void SortContact(Contact* pc);

contact.c文件(用来具体实现函数内部的功能)

#define  _CRT_SECURE_NO_WARNINGS 
#include"contact.h"
//静态版本
//初始化通讯录
void InitContact(Contact* pc)
{
  pc->sz = 0;
  memset(pc->data, 0, sizeof(pc->data));
}
//添加联系人
void AddContact(Contact* pc)
{
  //CheckContact(pc);
  if (pc->data == pc->sz)
  {
    printf("通讯录已满,无法添加联系人\n");
    return;
  }
  printf("请输入姓名:>");
  scanf("%s", pc->data[pc->sz].name);
  printf("请输入年龄:>");
  scanf("%d", &(pc->data[pc->sz].age));
  printf("请输入性别:>");
  scanf("%s", pc->data[pc->sz].sex);
  printf("请输入电话:>");
  scanf("%s", pc->data[pc->sz].tele);
  printf("请输入地址:>");
  scanf("%s", pc->data[pc->sz].addr);
  printf("添加成功\n");
  pc->sz++;
}
//显示通讯录
void ShowContact(const Contact* pc)
{
  printf("%-15s %-5s %-5s %-15s %-20s\n","姓名", "年龄", "性别", "电话", "地址");
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[i].name, pc->data[i].age,
      pc->data[i].sex,pc->data[i].tele, pc->data[i].addr);
  }
}
//查找姓名
static int Findname(Contact* pc, char name[])
{
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    if (0 == strcmp(pc->data[i].name, name))
    {
      return i;//找到返回i
    }
  }
  return -1;//没找到返回-1
}
//删除指定联系人
void DeleteContact(Contact* pc)
{
  if (0 == pc->sz)
  {
    printf("通讯录为空,无法删除联系人\n");
      return;
  }
  printf("请输入姓名:>");
  char name[MAX_NAME] = { 0 };
  scanf("%s",name);
  //查找姓名
  int pos = Findname(pc,name);
  //删除
  if (-1 == pos)
  {
    printf("联系人不存在\n");
    return;
  }
  int i = 0;
  for (i = pos; i < pc->sz; i++)
  {
    pc->data[pos] = pc->data[pos + 1];
  }
  printf("删除成功\n");
  pc->sz--;
}
//查找指定联系人
void SearchContact(const Contact* pc)
{
  char name[MAX_NAME] = { 0 };
  printf("请输入姓名:>");
  scanf("%s", name);
  int pos = Findname(pc, name);
  if (-1 == pos)
  {
    printf("联系人不存在\n");
    return;
  }
  printf("%-15s %-5s %-5s %-15s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
  printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[pos].name, pc->data[pos].age,
    pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
}
//修改指定联系人
void ModifyContact(Contact* pc)
{
  printf("请输入要修改人的姓名:>");
  char name[MAX_NAME] = { 0 };
  scanf("%s", name);
  int pos = Findname(pc, name);
  if (-1 == pos)
  {
    printf("联系人不存在\n");
    return;
  }
  printf("请输入姓名:>");
  scanf("%s", pc->data[pos].name);
  printf("请输入年龄:>");
  scanf("%d", &(pc->data[pos].age));
  printf("请输入性别:>");
  scanf("%s", pc->data[pos].sex);
  printf("请输入电话:>");
  scanf("%s", pc->data[pos].tele);
  printf("请输入地址:>");
  scanf("%s", pc->data[pos].addr);
  printf("修改成功\n");
}
//通过名字来排序
int cmp_by_name(const void* e1,const void* e2)
{
  return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
//排序通讯录
void SortContact(Contact* pc)
{
  qsort(pc->data, pc->sz,sizeof(PeoInfo),cmp_by_name);
  printf("排序成功\n");
}

二、动态通讯录

我们将动态的版本的通讯录修改为:默认能够存放3个人的信息,不够的话,每次增加2个人的信息

在contact.h文件中,我们宏定义 DEFAULT_SZ 表示 默认3个人的信息

INC_SZ 表示 容量不够时,每次增加2个人的信息

#define DEFAULT_SZ 3
#define INC_SZ 2

在contact.h文件中。声明结构体部分:我们要让动态申请的内存空间可大可小,一定需要malloc函数来申请空间,所以我们应该将结构体数组修改为一个结构体指针data,指针类型是PeoInfo*,data指向了存放数据的空间,定义sz变量用来记录当前通讯录中的信息个数,capacity变量用来记录通讯录的容量。

//动态版本的通讯录
typedef struct Contact
{
  PeoInfo* data;//data指向了存放数据的空间
  int sz;//记录通讯录中的有效信息个数
  int capacity;//记录通讯录的容量
}Contact;

在contact.c文件中,初始化通讯录函数里面的部分就需要大改了,我们需要首先开辟默认3个元素大小的空间,元素类型是PeoInfo* ,即malloc的参数为DEFAULT_SZ*sizeof(PeoInfo),


我们拿一个ptr的结构体指针接收,如果ptr为NULL,就打印报错的信息(strerror需要引用<string.h>头文件,errno需要引用<errno.h>),然后返回空,不为NULL的话,就将ptr赋给data,元素的起始地址就为data,sz初始化为0,capacity容量初始化为默认值。

//静态版本
初始化通讯录
//void InitContact(Contact* pc)
//{
//  pc->sz = 0;
//  memset(pc->data, 0, sizeof(pc->data));
//
//}
//动态版本
//初始化通讯录
void InitContact(Contact* pc)
{
  PeoInfo* ptr= (PeoInfo* )malloc(DEFAULT_SZ * sizeof(PeoInfo));
  if (ptr == NULL)
  {
    printf("通讯录初始化失败::%s", strerror(errno));
    return;
  }
  else
  {
    pc->data = ptr;
    pc->sz = 0;
    pc->capacity = DEFAULT_SZ;
  } 
}

在contact.c文件中,添加联系人函数里面我们也需要进行大幅度改动。pc->data这里就不是静态版本的数组了,我们直接将这个if语句给屏蔽掉,直接写一个CheckContact函数用来判断通讯录是否满了?是否需要扩容?

//添加联系人
void AddContact(Contact* pc)
{
  CheckContact(pc);
  /*if (pc->data == pc->sz)
  {
    printf("通讯录已满,无法添加联系人\n");
    return;
  }*/
  printf("请输入姓名:>");
  scanf("%s", pc->data[pc->sz].name);
  printf("请输入年龄:>");
  scanf("%d", &(pc->data[pc->sz].age));
  printf("请输入性别:>");
  scanf("%s", pc->data[pc->sz].sex);
  printf("请输入电话:>");
  scanf("%s", pc->data[pc->sz].tele);
  printf("请输入地址:>");
  scanf("%s", pc->data[pc->sz].addr);
  printf("添加成功\n");
  pc->sz++;
}

CheckContact函数


如果pc->sz 等于 pc->capacity ,即通讯录当前有效个人信息等于目前的容量时,说明通讯录已经满了,就需要扩容,此时就可以用realloc函数来扩容,pc->data表示通讯录的起始地址,新的大小为新的元素个数*每个元素的大小。返回值暂存在ptr指针之中,如果ptr为NULL,则打印报错的信息,不为NULL,则将ptr赋值给pc->data,容量+2,为了显示扩容效果,我们顺便打印当前容量值。

//检测通讯录是否满了
void CheckContact(Contact* pc)
{
  //如果通讯录满了 就增容
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr= (PeoInfo*)realloc(pc->data,(pc->capacity+ INC_SZ)*sizeof(PeoInfo));
    if (ptr == NULL)
    {
      printf("CheckContact::%s", strerror(errno));
      return;
    }
    else
    {
      pc->data = ptr;
      pc->capacity += INC_SZ;
      printf("增容成功,当前容量为:%d\n", pc->capacity);
    }
  }
  //没满直接跳过以上语句
}

最后,我们退出通讯录的时候,也可以将自己申请的内存空间给释放掉。我们在contact.h文件中声明一个DestroyContact函数表示销毁通讯录。

在contact.c文件下面接着编写代码:

//销毁通讯录
void DestroyContact(Contact* pc)
{
  free(pc->data);
  pc->data = NULL;
    pc->sz = 0;
    pc->capacity = 0;
  printf("释放成功...\n");
}

代码测试:

为了测试方便,作者直接将信息用数字测试,当我们输入三个有效信息之后,此时有效空间等于当前的通讯录容量了,当我们再次选择1,继续添加联系人,就“显示增容成功,当前容量为5”。

image.png

三、代码整理

将代码整理在下面,需要的自取呐~

test.c文件

#include "contact.h"
void Menu()
{
  printf("********************************\n");
  printf("******   0.exit 1.add     ******\n");
  printf("******  2.delete  3.modify******\n");
  printf("******  4.search 5.show    *****\n");
  printf("******      6.sort         *****\n");
  printf("********************************\n");
}
enum Option
{
  EXIT,
  ADD,
  DELETE,
  MODIFY,
  SEARCH,
  SHOW,
  SORT
};
int main()
{
  Contact con;//创建一个名为con的结构体变量
  InitContact(&con);
  int input = 0;
  do
  {
    Menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case ADD:
      AddContact(&con);
      break;
    case DELETE:
      DeleteContact(&con);
      break;
    case MODIFY:
      ModifyContact(&con);
      break;
    case SEARCH:
      SearchContact(&con);
      break;
    case SHOW:
      ShowContact(&con);
      break;
    case SORT:
      SortContact(&con);
      break;
    case EXIT:
      DestroyContact(&con);
      printf("退出通讯录\n");
      break;
    default:
      printf("输入有误,请重新输入\n");
      break;
    }
  } while (input);
}

contact.h文件

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 15
#define MAX_ADDR 20
//#define MAX 20 
#define DEFAULT_SZ 3
#define INC_SZ 2
//创建个人信息
typedef struct PeoInfo
{
  char name[MAX_NAME];
  int age;
  char sex[MAX_SEX];
  char tele[MAX_TELE];
  char addr[MAX_ADDR];
}PeoInfo;
静态版本的通讯录
声明一个通讯录结构体
//typedef struct Contact
//{
//  PeoInfo data[MAX];
//  int sz;
//}Contact;
//动态版本的通讯录
typedef struct Contact
{
  PeoInfo* data;//data指向了存放数据的空间
  int sz;//记录通讯录中的有效信息个数
  int capacity;//记录通讯录的容量
}Contact;
//初始化结构体
void InitContact(Contact* pc);
//添加联系人
void AddContact(Contact* pc);
//显示通讯录
void ShowContact(const Contact* pc);
//删除指定联系人
void DeleteContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//排序通讯录
void SortContact(Contact* pc);
//销毁通讯录
void DestroyContact(Contact* pc);

contact.c文件

#include"contact.h"
//静态版本
初始化通讯录
//void InitContact(Contact* pc)
//{
//  pc->sz = 0;
//  memset(pc->data, 0, sizeof(pc->data));
//
//}
//动态版本
//初始化通讯录
void InitContact(Contact* pc)
{
  PeoInfo* ptr= (PeoInfo* )malloc(DEFAULT_SZ * sizeof(PeoInfo));
  if (ptr == NULL)
  {
    printf("通讯录初始化失败::%s", strerror(errno));
    return;
  }
  else
  {
    pc->data = ptr;
    pc->sz = 0;
    pc->capacity = DEFAULT_SZ;
  } 
}
//检测通讯录是否满了
void CheckContact(Contact* pc)
{
  //如果通讯录满了 就增容
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr= (PeoInfo*)realloc(pc->data,(pc->capacity+ INC_SZ)*sizeof(PeoInfo));
    if (ptr == NULL)
    {
      printf("CheckContact::%s", strerror(errno));
      return;
    }
    else
    {
      pc->data = ptr;
      pc->capacity += INC_SZ;
      printf("增容成功,当前容量为:%d\n", pc->capacity);
    }
  }
  //没满直接跳过以上语句
}
//添加联系人
void AddContact(Contact* pc)
{
  CheckContact(pc);
  /*if (pc->data == pc->sz)
  {
    printf("通讯录已满,无法添加联系人\n");
    return;
  }*/
  printf("请输入姓名:>");
  scanf("%s", pc->data[pc->sz].name);
  printf("请输入年龄:>");
  scanf("%d", &(pc->data[pc->sz].age));
  printf("请输入性别:>");
  scanf("%s", pc->data[pc->sz].sex);
  printf("请输入电话:>");
  scanf("%s", pc->data[pc->sz].tele);
  printf("请输入地址:>");
  scanf("%s", pc->data[pc->sz].addr);
  printf("添加成功\n");
  pc->sz++;
}
//显示通讯录
void ShowContact(const Contact* pc)
{
  printf("%-15s %-5s %-5s %-15s %-20s\n","姓名", "年龄", "性别", "电话", "地址");
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[i].name, pc->data[i].age,
      pc->data[i].sex,pc->data[i].tele, pc->data[i].addr);
  }
}
//查找姓名=
static int Findname(Contact* pc, char name[])
{
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    if (0 == strcmp(pc->data[i].name, name))
    {
      return i;//找到返回i
    }
  }
  return -1;//没找到返回-1
}
//删除指定联系人
void DeleteContact(Contact* pc)
{
  if (0 == pc->sz)
  {
    printf("通讯录为空,无法删除联系人\n");
      return;
  }
  printf("请输入姓名:>");
  char name[MAX_NAME] = { 0 };
  scanf("%s",name);
  //查找姓名
  int pos = Findname(pc,name);
  //删除
  if (-1 == pos)
  {
    printf("联系人不存在\n");
    return;
  }
  int i = 0;
  for (i = pos; i < pc->sz; i++)
  {
    pc->data[pos] = pc->data[pos + 1];
  }
  printf("删除成功\n");
  pc->sz--;
}
//查找指定联系人
void SearchContact(const Contact* pc)
{
  char name[MAX_NAME] = { 0 };
  printf("请输入姓名:>");
  scanf("%s", name);
  int pos = Findname(pc, name);
  if (-1 == pos)
  {
    printf("联系人不存在\n");
    return;
  }
  printf("%-15s %-5s %-5s %-15s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
  printf("%-15s %-5d %-5s %-15s %-20s\n", pc->data[pos].name, pc->data[pos].age,
    pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
}
//修改指定联系人
void ModifyContact(Contact* pc)
{
  printf("请输入要修改人的姓名:>");
  char name[MAX_NAME] = { 0 };
  scanf("%s", name);
  int pos = Findname(pc, name);
  if (-1 == pos)
  {
    printf("联系人不存在\n");
    return;
  }
  printf("请输入姓名:>");
  scanf("%s", pc->data[pos].name);
  printf("请输入年龄:>");
  scanf("%d", &(pc->data[pos].age));
  printf("请输入性别:>");
  scanf("%s", pc->data[pos].sex);
  printf("请输入电话:>");
  scanf("%s", pc->data[pos].tele);
  printf("请输入地址:>");
  scanf("%s", pc->data[pos].addr);
  printf("修改成功\n");
}
//通过名字来排序
int cmp_by_name(const void* e1,const void* e2)
{
  return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
//排序通讯录
void SortContact(Contact* pc)
{
  qsort(pc->data, pc->sz,sizeof(PeoInfo),cmp_by_name);
  printf("排序成功\n");
}
//销毁通讯录
void DestroyContact(Contact* pc)
{
  free(pc->data);
  pc->data = NULL;
  printf("释放成功...\n");
}
目录
相关文章
|
3天前
|
C语言
用c语言实现一个通讯录
用c语言实现一个通讯录
12 0
|
3天前
|
存储 C语言
C语言详解【通讯录的实现】
C语言详解【通讯录的实现】
|
3天前
|
存储 XML JSON
【C语言-通讯录的分析与实现】
我们以手机里面的通讯录为例,说明通讯录主要有以下什么功能,我们先用一张简单的图来说明一下
24 0
|
5月前
|
C语言
C语言实现通讯录--动态版
C语言实现通讯录--动态版
35 0
|
3天前
|
C语言
【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化
动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。
38 0
|
3天前
|
C语言
C语言进阶第八课 --------通讯录的实现
C语言进阶第八课 --------通讯录的实现
|
3天前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
|
3天前
通讯录(C语言版)
通讯录(C语言版)
|
3天前
|
程序员 C语言
【C语言实战项目】通讯录(动态增容版)
【C语言实战项目】通讯录(动态增容版)
23 0
|
3天前
|
存储 编译器 C语言
【C语言实战项目】通讯录
【C语言实战项目】通讯录
29 0