C语言实验-动态顺序表实现简易通讯录(一)

简介: 本文介绍了使用C语言模拟实现通讯录的步骤,包括使用动态顺序表存储联系人信息,每个联系人包含姓名、性别、电话和住址。功能包括新增、查找、删除、修改和查看联系人信息,以及按关键词排序。代码示例展示了如何定义联系人结构体、通讯录顺序表,以及主函数中的菜单选择和输入处理。文章还强调了在读取用户输入时避免出现死循环的问题,并提供了初始化和销毁通讯录的函数,以及如何判断和增加通讯录容量的方法。

一、前言

本文介绍用C语言模拟一个通讯录,用来存储联系人的信息。采取数据结构中的动弹顺序表来实现。


每个联系人的信息包括:姓名、性别、电话、住址。


通讯录的功能包括:


  1. 新增联系人信息
  2. 按姓名查找联系人
  3. 删除联系人信息
  4. 修改指定联系人的指定信息
  5. 查看所有联系人信息
  6. 按指定关键字排序联系人信息



二、实现通讯录


1.定义联系人


通讯录存放联系人的信息,因此顺序表中的每一个元素应是一个“联系人”的结构体类型。


代码如下:


typedef struct People
{
  char name[20];  //姓名
  char sex[6];  //性别
  char tel[15]; //电话
  char addr[50];  //住址
}People;


2.定义通讯录顺序表


采用顺序表来实现通讯录。一个通讯录包含:存储的元素内容、通讯录表长与通讯录表容量。表长用于记录已存储的联系人个数,表的容量用于控制通讯录能否正常使用、是否需要增容等。


代码如下:


typedef struct Contact 
{
  People* arr;
  int size;
  int capacity;
}Contact;


注意,这里的数组arr的类型是联系人类型,也就是People类型,因为它存放的内容是每一个联系人的信息。


3.通讯录菜单及主函数


菜单与main函数


菜单包含了我们要实现的功能提示。将菜单封装成函数,在主函数中调用。


代码如下:


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");
  printf("\n");
}


用户通过选择功能前不同的数字代号,来进入对应的功能。在主函数中,我们可以通过switch-case语句来实现多分支,如当用户输入数字1时,进入add功能。


枚举主函数内部选项


为了增强代码的可读性,我们也可以用枚举类型定义以上菜单中的各个功能。在如下的枚举中,从texit到sort这七个变量分别代表整型数字0到数字6.


enum Choice
{
  texit,    //0    exit与c语言函数重名,因此需更改一下变量名
  add,    //1
  del,    //2
  search,    //3
  modify,    //4
  show,    //5
  sort    //6
};


因此,在main函数的switch-case控制中,我们可以这样写:用枚举中定义的7个变量分别代替0到6这七个数字作为switch-case的入口。


  int input = 0;
 
  do
  {
    menu();
    printf("请输入选项:>");
    scanf("%d", &input);
 
    switch (input)
    {
    case add:
      //add
      break;
    case del:
      //del
      break;
    case search:
      //search
      break;
    case modify:
      //modify
      break;
    case show:
      //show
      break;
    case sort:
      //sort
      break;
    case texit:
      //texit
      break;
    default:
      printf("请输入正确的选项!\n");
      break;
    }
    fflush(stdin);
    printf("\n");
  } while (input);
  
  return 0;
}


fflush(stdin)清空缓冲区


在上面代码中,在单个功能结束后调用了fflush(stdin)函数。该函数是C语言的清空缓冲区函数。


在输入input选项时,scanf("%d",&input)语句只有从缓冲区中读取到整型数字时,读取成功。但如果用户误触,没有输入整型数字,而输入了浮点数或其它非法数据,scanf会发生读取错误,即实际读入的数据与%d不匹配。而此时,缓冲区中的非法数据并没有因为scanf的读取而被清除。在第一次do-while循环结束、第二次循环进入时,scanf仍然会因为读取到缓冲区中的非法数据而发生读取错误。


只要缓冲区内的非法数据不清楚,scanf就永远无法读到正确的数据。这里可能会发生do-while的死循环,因此用fflush(stdin)进行规避。



如输入7.3时,会发生死循环



输入1.2,虽然不会发生死循环,但也会因为读取错误导致程序无法正常运行


这里对fflush(stdin)函数作简要介绍,并且提醒一下大家scanf的数据读取问题。


4.初始化与销毁通讯录

初始化


初始化通讯录可以有两个版本:初始化并分配顺序表空间或仅初始化,不分配顺序表空间(分配空间的任务在对顺序表进行插入时再执行)。


代码如下:


版本一:初始化并分配顺序表空间


#define DEFAULT_SZ 3
 
void Init(Contact* con)
{
  assert(con);
 
  con->arr = (People*)malloc(DEFAULT_SZ * sizeof(People));
  if (con->arr == NULL)
  {
    perror("Init()");
    return;
  }
  con->size = 0;
  con->capacity = DEFAULT_SZ;
}


版本二:仅初始化不分配空间


1.void Init(Contact* con)
{
  con->arr = NULL;
  con->size = con->capacity = 0;
}



*下面将采取版本二对应的思路实现剩余代码。


销毁


1.//7-销毁
void destroyContact(Contact *con)
{
  assert(con);
  free(con->arr);
  con->arr = NULL;
  con->size = con->capacity = 0;
  printf("销毁成功!\n");
}


在exit之前,必须先销毁,否则会造成内存泄漏。


5.新增联系人

判断增容


若表长size与表容量capacity相等时,意味着顺序表满,应当增容。


增容代码如下:


//判断增容
static void CheckCapacity(Contact* con)
{
  assert(con);
 
  if (con->capacity == con->size)    //判断通讯录是否已满
  {
        //由于初始化表时,并未给表分配空间,因此若capacity为0,则应先分配空间
    int newCapacity = con->capacity == 0 ? 4 : 2 * con->capacity;
 
        //realloc原地增容
    People* tmp = (People*)realloc(con->arr, sizeof(People) * newCapacity);
    if (tmp == NULL)
    {
      perror("CheckCapacity");
      exit(-1);
    }
    else
    {
      con->arr = tmp;
      con->capacity = newCapacity;
    }
  }
}



增容的思路是当表满后,顺序表的容量扩展为原表的两倍。但由于初始化通讯录顺序表时,并未给顺序表分配空间,因此最开始表capacity为0.此时2*0结果还是0,无法达到增容的效果,程序会出错。因此先对newCapacity进行判断,若newCapacity为0,则先给它赋一个初始值。若newCapacity不为0,那么扩容到原来的两倍。


此外,扩容应当是在原数组的基础上扩张。因此要用realloc来原地扩容。realloc的原理是当原数组后还有连续可用空间时,直接在原数组后开辟空间,仍然返回原数组地址。


若用malloc,则会重新开辟一片内存空间,原数组中的内容会丢失,因此不妥。


添加联系人


在判断增容过后,就能安全地添加联系人了。我们通过con->arr[con->size].成员名 来输入联系人信息。


con是一个指向通讯录的指针,通讯录中的arr数组存储的是联系人People的信息。


con->arr即可访问到通讯录中的People信息。


由于是顺序表,通过con->size即可访问表中元素。表中每个元素都是People类型,因此con->arr[con->size]表示的正是一个People类型的结构体,这时通过成员访问符 . 即可对相应的信息进行输入。


添加联系人的代码如下:


//1-添加
void addContact(Contact *con)
{
  //判断增容
  CheckCapacity(con);
 
  //添加内容
  printf("姓名:");
  scanf(" %s", con->arr[con->size].name);
  printf("性别:");
  scanf(" %s", con->arr[con->size].sex);
  printf("电话:");
  scanf(" %s", con->arr[con->size].tel);
  printf("住址:");
  scanf(" %s", con->arr[con->size].addr);
 
  con->size++;    //添加完毕后个数要+1
  printf("添加联系人成功!\n");
}



C语言实验-动态顺序表实现简易通讯录(二)

+https://developer.aliyun.com/article/1519325?spm=a2c6h.13148508.setting.15.25634f0e0MQf1l

相关文章
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
56 4
|
1月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
82 3
|
2月前
|
C语言
大学生期末C语言实验(学生成绩和鞍点)
大学生期末C语言实验(学生成绩和鞍点)
269 0
大学生期末C语言实验(学生成绩和鞍点)
|
2月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
38 2
|
2月前
|
C语言
链式顺序表实现(C语言描述)
本文介绍了如何在C语言中实现链式顺序表,包括数据结构的定义、节点的创建、数据的插入和删除以及链表的打印和销毁。
49 2
|
2月前
|
C语言
顺序表数组法构建(C语言描述)
如何使用C语言通过数组方法构建有序顺序表,包括顺序表的创建、插入、删除和打印等。
22 2
|
2月前
|
存储 C语言
手把手教你用C语言实现通讯录管理系统
手把手教你用C语言实现通讯录管理系统
|
3月前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
23天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
48 10
|
23天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
43 9