《通讯录》思路及代码实现详解

简介: 《通讯录》思路及代码实现详解

一、通讯录功能实现的详细描述


在日常生活中,通讯录是我们每个人都非常熟悉的了。最常见的就是手机中的电话薄。我们就根据手机中的电话薄的功能来模拟实现一个通讯录。我们先看实现的通讯录都有哪些具体细节:


打印一个菜单,提供用户选择功能;

添加联系人信息;

删除联系人信息;

查询联系人信息;

修改联系人信息;

显示所有联系人信息;

对所有联系人信息进行排序整理;

删除所有联系人信息;

操作完毕可选择退出。

 以上就是实现的通讯录的整体细节与模板,具体每个功能的具体实现思路及细节我们接着往下看,我会给大家一一实现与讲解。


二、通讯录的代码及思路实现

2、1 定义联系人结构体


在整体实现之前,我们需要先把联系人结构体实现出来。我们这个结构体需要包含联系人的所有信息。同时,我们为了动态实现通讯录,我们需要再定义一个结构体,包含一个联系人结构体指针、记录存储的多少个联系人的一个变量和一个记录联系人结构体的容量。我们具体看代码实现。

 

#define MAX_NAME 20
#define MAX_ADDR 20
#define MAX_TELE 12
#define MAX_SEX 5
#define INIT_SZ 50
#define INCREASE_SZ 20
typedef struct PeoInfo
{
  char name[MAX_NAME];
  int age;
  char sex[MAX_SEX];
  char addr[MAX_ADDR];
  char tele[MAX_TELE];
}PeoInfo;
typedef struct Contact
{
  PeoInfo* data;
  int size;
  int capacity;
}Contact;



2、2 初始化就结构体与释放动态开辟空间的实现


 在这里我们把初始化就结构体与释放动态开辟空间的实现放到一块实现,因为他们本身就可以看做一对。在初始化结构体时,我们需要初始给出通讯录可存储多少个联系人,还要给出增长空间的大小。我们来看代码:

void InitContact(Contact* ps)
{
  PeoInfo* str = (PeoInfo*)calloc(INIT_SZ, sizeof(PeoInfo));
  if (str == NULL)
  {
    perror("InitContact::calloc");
    return;
  }
  ps->data = str;
  ps->size = 0;
  ps->capacity = INIT_SZ;
}
void DestoryContact(Contact* ps)
{
  free(ps->data);
  ps->capacity = 0;
  ps->size = 0;
  ps->data = NULL;
  ps = NULL;
}

2、3 菜单打印


菜单的打印是我们必须第一个实现的,也是我们实现整个通讯录的参照。 菜单的打印需要简单明了即可。且实现比较简单。注意要单独放在一个自定义函数中,让主函数中的代码尽量减少,方便观察。

void menu()
{
  printf("********************************\n");
  printf("****   1.Add      2.Del     ****\n");
  printf("****   3.Reserch  4.Modify  ****\n");
  printf("****   5.Show     6.Sort    ****\n");
  printf("****   7.DelAll   0.Exit    ****\n");
  printf("********************************\n");
}


2、4 添加联系人信息


 再添加联系人之前,我们要先判断通讯录是否为满。如果满了的话我们需要进行扩容,再进行添加联系人。注意,为了输出格式简介,我们这里在添加联系人时就进行了格式化添加。具体我们结合这代码一起理解一下。

void check_capacity(Contact* ps)
{
  if (ps->capacity == ps->size)
  {
    PeoInfo* str = (PeoInfo*)realloc(ps->data, sizeof(PeoInfo) * (ps->capacity + INCREASE_SZ));
    if (str == NULL)
    {
      perror("check_capacity::realloc");
      return;
    }
    ps->data = str;
    ps->capacity += INCREASE_SZ;
    printf("增容成功\n");
  }
}
void AddContact(Contact* ps)
{
  check_capacity(ps);
  printf("请输入要添加的姓名:");
  scanf("%s", ps->data[ps->size].name);
  printf("请输入要添加的年龄:");
  scanf("%d", &ps->data[ps->size].age);
  printf("请输入要添加的性别:");
  scanf("%s", ps->data[ps->size].sex);
  printf("请输入要添加的地址:");
  scanf("%s", ps->data[ps->size].addr);
  printf("请输入要添加的电话:");
  scanf("%s", ps->data[ps->size].tele);
  ps->size++;
}

2、5 删除联系人信息


我们这里是根据用户所输入的联系人姓名进行删除的,所以我们先要判断用户所输入的联系人是否存在。当然,如果通讯录为空的话,我们就直接可以知道删除失败的。由于后面多个功能都需要查找联系人,所以我们这里把查找联系人独立分装成一个函数,以便我们后续的使用。

int find(Contact* ps, char* name)
{
  for (int i = 0; i < ps->size; i++)
  {
    if (strcmp(ps->data[i].name, name) == 0)
    {
      return i;
    }
  }
  return -1;
}
void DelContact(Contact* ps)
{
  if (ps->size == 0)
  {
    printf("通讯录为空,删除失败\n");
    return;
  }
  char name[MAX_NAME];
  printf("请输入要删除人的姓名:");
  scanf("%s", name);
  int ret = find(ps, name);
  if (ret == -1)
  {
    printf("索要删除的人不存在哦\n");
    return;
  }
  int i = 0;
  for (i = ret; i < ps->size - 1; i++)
  {
    ps->data[i] = ps->data[i + 1];
  }
  ps->size--;
  printf("删除成功\n");
}


2、6 查询联系人信息


我们这里的查询寻联系人与find()函数并不相同,但是又有联系。我们需要查找出联系人,如果该联系人存在,我们就输出该联系人的信息,注意输出格式简洁。如果不存在,我们就提示用户该联系人不存在。代码的实现也相对简单,我们直接看代码的实现:

void ReserchContact(Contact* ps)
{
  char name[MAX_NAME];
  printf("请输入要查询人的姓名:");
  scanf("%s", name);
  int ret = find(ps, name);
  if (ret == -1)
  {
    printf("通讯录中没有%s的信息哦\n", name);
    return;
  }
  printf("%-20s%-20s%-15s%-20s%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
  printf("%-20s%-20d%-15s%-20s%-12s\n", ps->data[ret].name, ps->data[ret].age, ps->data[ret].sex, ps->data[ret].addr, ps->data[ret].tele);
}


2、7 修改联系人信息


修改联系人信息与查询联系人信息思路大同小异,代码实现也是相似。修改联系人信息我们先要找到联系人,然后再提示用户输入修改联系人的信息。我们直接看代码:

void ModifyContact(Contact* ps)
{
  char name[MAX_NAME];
  printf("请输入要修改人的姓名:");
  scanf("%s", name);
  int ret = find(ps, name);
  if (ret == -1)
  {
    printf("通讯录中没有%s的信息哦\n", name);
    return;
  }
  printf("请输入姓名:");
  scanf("%s", ps->data[ret].name);
  printf("请输入年龄:");
  scanf("%d", &ps->data[ret].age);
  printf("请输入性别:");
  scanf("%s", ps->data[ret].sex);
  printf("请输入地址:");
  scanf("%s", ps->data[ret].addr);
  printf("请输入电话:");
  scanf("%s", ps->data[ret].tele);
  printf("修改成功\n");
}


2、8 打印所有联系人信息

 我们只需判断通讯录是否为空,如果为空就提示用户。不为空我们就直接遍历打印即可。实现相对较为简单,我们直接看代码:

void ShowContact(Contact* ps)
{
  printf("%-20s%-20s%-15s%-20s%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
  int i = 0;
  for (i = 0; i < ps->size; i++)
  {
    printf("%-20s%-20d%-15s%-20s%-12s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].addr, ps->data[i].tele);
  }
}



2、9 排序整理联系人信息

 我们先首先要给出一个排序菜单,提供给客户选择根据联系人的哪项信息来进行排序,我们这里利用qsort函数实现按排序。整体还是简单的。我们看代码实现:

void SortMenu()
{
  printf("\n");
  printf("********************************\n");
  printf("****   1.name     2.age     ****\n");
  printf("****   3.addr     4.tele    ****\n");
  printf("****   5.sex                ****\n");
  printf("********************************\n");
  printf("\n");
}
int cmp_by_name(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).name, (*((PeoInfo*)e2)).name);
}
int cmp_by_age(const void* e1, const void* e2)
{
  return (*((PeoInfo*)e1)).age - (*((PeoInfo*)e2)).age;
}
int cmp_by_addr(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).addr, (*((PeoInfo*)e2)).addr);
}
int cmp_by_tele(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).tele, (*((PeoInfo*)e2)).tele);
}
int cmp_by_sex(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).sex, (*((PeoInfo*)e2)).sex);
}
void SortContact(Contact* ps)
{
  int input = 0;
  if (ps->size == 0)
  {
    printf("通讯录为空,不用排序哦\n");
  }
  do
  {
    SortMenu();
    printf("请选择要根据什么排序:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_name);
      break;
    case 2:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_age);
      break;
    case 3:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_addr);
      break;
    case 4:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_tele);
      break;
    case 5:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_sex);
      break;
    default:
      printf("输入有误,请重新输入:");
    }
  } while (0);
}



2、10 删除所有联系人信息

 这个是最简单了,我们只需要先判断通讯录是否为空,不为空我们直接把size置0即可。

void DelAllContact(Contact* ps)
{
  if (ps->size == 0)
  {
    printf("通讯录已经为空了哦\n");
    return;
  }
  ps->size = 0;
  printf("删除成功\n");
}


三、通讯录代码的整合


由于代码量相对来说有一点多,所以我们就将函数的声明的定义分开,这样有利于提高代码的可读性,同时会保持一个良好的思路,且方便编写代码。


 我们将函数的声明放在单独的一个contact.h的头文件,函数的实现放在一个单独的contact.c源文件,函数的主方法及调用放在另一个单独的test.c源文件。


contact.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#define MAX 100
#define MAX_NAME 20
#define MAX_ADDR 20
#define MAX_TELE 12
#define MAX_SEX 5
#define INIT_SZ 2
#define INCREASE_SZ 1
typedef struct PeoInfo
{
  char name[MAX_NAME];
  int age;
  char sex[MAX_SEX];
  char addr[MAX_ADDR];
  char tele[MAX_TELE];
}PeoInfo;
typedef struct Contact
{
  PeoInfo* data;
  int size;
  int capacity;
}Contact;
//初始化
void InitContact(Contact* ps);
//释放动态开辟空间
void DestoryContact(Contact* ps);
//菜单
void menu();
//添加个人信息
void AddContact(Contact* ps);
//删除个人信息
void DelContact(Contact* s);
//查询个人信息
void ReserchContact(Contact* s);
//修改个人信息
void ModifyContact(Contact* s);
//查看整个通讯录
void ShowContact(Contact* s);
//对通讯录进行排序
void SortContact(Contact* s);
//排序菜单
void SortMenu();
//删除整个通讯录信息
void DelAllContact(Contact* s);



contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Contact.h"
void InitContact(Contact* ps)
{
  PeoInfo* str = (PeoInfo*)calloc(INIT_SZ, sizeof(PeoInfo));
  if (str == NULL)
  {
    perror("InitContact::calloc");
    return;
  }
  ps->data = str;
  ps->size = 0;
  ps->capacity = INIT_SZ;
}
void DestoryContact(Contact* ps)
{
  free(ps->data);
  ps->capacity = 0;
  ps->size = 0;
  ps->data = NULL;
  ps = NULL;
}
void menu()
{
  printf("********************************\n");
  printf("****   1.Add      2.Del     ****\n");
  printf("****   3.Reserch  4.Modify  ****\n");
  printf("****   5.Show     6.Sort    ****\n");
  printf("****   7.DelAll   0.Exit    ****\n");
  printf("********************************\n");
}
int find(Contact* ps, char* name)
{
  for (int i = 0; i < ps->size; i++)
  {
    if (strcmp(ps->data[i].name, name) == 0)
    {
      return i;
    }
  }
  return -1;
}
void check_capacity(Contact* ps)
{
  if (ps->capacity == ps->size)
  {
    PeoInfo* str = (PeoInfo*)realloc(ps->data, sizeof(PeoInfo) * (ps->capacity + INCREASE_SZ));
    if (str == NULL)
    {
      perror("check_capacity::realloc");
      return;
    }
    ps->data = str;
    ps->capacity += INCREASE_SZ;
    printf("增容成功\n");
  }
}
void AddContact(Contact* ps)
{
  check_capacity(ps);
  printf("请输入要添加的姓名:");
  scanf("%s", ps->data[ps->size].name);
  printf("请输入要添加的年龄:");
  scanf("%d", &ps->data[ps->size].age);
  printf("请输入要添加的性别:");
  scanf("%s", ps->data[ps->size].sex);
  printf("请输入要添加的地址:");
  scanf("%s", ps->data[ps->size].addr);
  printf("请输入要添加的电话:");
  scanf("%s", ps->data[ps->size].tele);
  ps->size++;
}
void DelContact(Contact* ps)
{
  if (ps->size == 0)
  {
    printf("通讯录为空,删除失败\n");
    return;
  }
  char name[MAX_NAME];
  printf("请输入要删除人的姓名:");
  scanf("%s", name);
  int ret = find(ps, name);
  if (ret == -1)
  {
    printf("索要删除的人不存在哦\n");
    return;
  }
  int i = 0;
  for (i = ret; i < ps->size - 1; i++)
  {
    ps->data[i] = ps->data[i + 1];
  }
  ps->size--;
  printf("删除成功\n");
}
void ReserchContact(Contact* ps)
{
  char name[MAX_NAME];
  printf("请输入要查询人的姓名:");
  scanf("%s", name);
  int ret = find(ps, name);
  if (ret == -1)
  {
    printf("通讯录中没有%s的信息哦\n", name);
    return;
  }
  printf("%-20s%-20s%-15s%-20s%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
  printf("%-20s%-20d%-15s%-20s%-12s\n", ps->data[ret].name, ps->data[ret].age, ps->data[ret].sex, ps->data[ret].addr, ps->data[ret].tele);
}
void ModifyContact(Contact* ps)
{
  char name[MAX_NAME];
  printf("请输入要修改人的姓名:");
  scanf("%s", name);
  int ret = find(ps, name);
  if (ret == -1)
  {
    printf("通讯录中没有%s的信息哦\n", name);
    return;
  }
  printf("请输入姓名:");
  scanf("%s", ps->data[ret].name);
  printf("请输入年龄:");
  scanf("%d", &ps->data[ret].age);
  printf("请输入性别:");
  scanf("%s", ps->data[ret].sex);
  printf("请输入地址:");
  scanf("%s", ps->data[ret].addr);
  printf("请输入电话:");
  scanf("%s", ps->data[ret].tele);
  printf("修改成功\n");
}
void ShowContact(Contact* ps)
{
  printf("%-20s%-20s%-15s%-20s%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
  int i = 0;
  for (i = 0; i < ps->size; i++)
  {
    printf("%-20s%-20d%-15s%-20s%-12s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].addr, ps->data[i].tele);
  }
}
void SortMenu()
{
  printf("\n");
  printf("********************************\n");
  printf("****   1.name     2.age     ****\n");
  printf("****   3.addr     4.tele    ****\n");
  printf("****   5.sex                ****\n");
  printf("********************************\n");
  printf("\n");
}
int cmp_by_name(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).name, (*((PeoInfo*)e2)).name);
}
int cmp_by_age(const void* e1, const void* e2)
{
  return (*((PeoInfo*)e1)).age - (*((PeoInfo*)e2)).age;
}
int cmp_by_addr(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).addr, (*((PeoInfo*)e2)).addr);
}
int cmp_by_tele(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).tele, (*((PeoInfo*)e2)).tele);
}
int cmp_by_sex(const void* e1, const void* e2)
{
  return strcmp((*((PeoInfo*)e1)).sex, (*((PeoInfo*)e2)).sex);
}
void SortContact(Contact* ps)
{
  int input = 0;
  if (ps->size == 0)
  {
    printf("通讯录为空,不用排序哦\n");
  }
  do
  {
    SortMenu();
    printf("请选择要根据什么排序:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_name);
      break;
    case 2:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_age);
      break;
    case 3:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_addr);
      break;
    case 4:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_tele);
      break;
    case 5:
      qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_by_sex);
      break;
    default:
      printf("输入有误,请重新输入:");
    }
  } while (0);
}
void DelAllContact(Contact* ps)
{
  if (ps->size == 0)
  {
    printf("通讯录已经为空了哦\n");
    return;
  }
  ps->size = 0;
  printf("删除成功\n");
}


test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Contact.h"
int main()
{
  int input = 0;
  Contact s;
  InitContact(&s);
  do
  {
    menu();
    printf("请选择->");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      AddContact(&s);
      break;
    case 2:
      DelContact(&s);
      break;
    case 3:
      ReserchContact(&s);
      break;
    case 4:
      ModifyContact(&s);
      break;
    case 5:
      ShowContact(&s);
      break;
    case 6:
      SortContact(&s);
      break;
    case 7:
      DelAllContact(&s);
      break;
    case 0:
      DestoryContact(&s);
      printf("退出通讯录\n");
      break;
    default:
      printf("输入有误,请重新输入:");
    }
  } while (input);
  return 0;
}


四、总结


我们要实现通讯录前,我们应该有一个整体的模板和思路,这样对于我们后续的实现比较有利。整个实现的过程相对来说并不是很难,中间在添加联系人和删除联系人的时候有几个细节是需要我们注意的。当我们有了整体思路,实现起来也就不是问题了。


 对通讯录的实现就到这里,希望本篇文章会对你有所帮助,感谢阅读ovo~


相关文章
|
Android开发 容器
Android UI设计: 什么是View和ViewGroup?
Android UI设计: 什么是View和ViewGroup?
552 0
|
6月前
|
存储 SQL 关系型数据库
HarmonyOS Next快速入门:RelationalStore关系型数据库
本课程《HarmonyOS Next快速入门》涵盖HarmonyOS应用开发中的关系型数据库使用,介绍基于SQLite的持久化存储、适用场景及开发实践,适用于教育与初学者。
263 0
|
7月前
|
设计模式 SQL 安全
并发设计模式实战系列(13):双重检查锁定(Double-Checked Locking)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十三章,废话不多说直接开始~
456 0
|
数据采集 供应链 搜索推荐
商业案例 I AllData数据中台商业版落地实践
杭州奥零数据科技有限公司成立于2023年,专注于数据中台业务,维护开源项目AllData并提供商业版解决方案。AllData提供数据集成、存储、开发、治理及BI展示等一站式服务,支持AI大模型应用,助力企业高效利用数据价值。
|
7月前
|
XML Java Android开发
Android 动画之帧动画 + 补间动画 + 属性动画
本文介绍了Android开发中的三种动画类型:帧动画、补间动画和属性动画。帧动画通过依次播放一系列静态图片实现动态效果,支持Java代码与XML两种实现方式。补间动画基于起始和结束位置自动生成过渡效果,涵盖透明度、位移、旋转、缩放及组合动画等多种形式,并可搭配插值器优化动画过程。属性动画则通过改变对象属性实现动画,支持透明度、位移、旋转、缩放及组合动画,灵活性更高且适用于更复杂的场景。文中提供了详细的代码示例,帮助开发者快速上手。
428 15
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
195 2
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
526 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
算法 JavaScript 前端开发
程序员需要掌握的 5种编程范式!
本文介绍了编程范式的基本概念及其重要性,详细解析了命令式编程与声明式编程两种主要范式。命令式编程包括面向过程与面向对象编程,强调逐步执行与模块化;声明式编程涵盖函数式、逻辑及响应式编程,注重描述目标而非具体步骤。通过对比各种范式的优缺点及示例代码,帮助读者理解不同场景下的适用性。
532 2
|
消息中间件 存储 网络协议
操作系统的心脏:深入理解进程间通信(IPC)机制
在现代计算机系统中,操作系统扮演着至关重要的角色,而进程间通信(IPC)作为操作系统的核心功能之一,极大地影响着系统的性能和稳定性。本文将通过浅显易懂的语言,详细探讨进程间通信的基本原理、主要类型及其实际应用,旨在为读者提供一个清晰且全面的理解和认识。 ##
841 1
|
存储 C语言
一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
537 5

热门文章

最新文章