静态通讯录,适合初学者的手把手一条龙讲解

简介: 静态通讯录,适合初学者的手把手一条龙讲解

数据结构的顺序表和链表是一个比较困难的点,初见会让我们觉得有点困难,正巧C语言中有一个类似于顺序表和链表的小程序——通讯录。我们今天就来讲一讲通讯录的实现,也有利于之后顺序表和链表的学习。


我们需要用C语言模拟一个通讯录可以用来存储100个人的信息

每个人的信息包括:

姓名、电话、性别、住址、年龄

功能包括:

  1. 新增联系人
  2. 查找联系人
  3. 删除联系人
  4. 修改联系人
  5. 查看所有联系人
  6. 以名字排序所有联系人

0.通讯录的初始化


尽管我们这是一个基础的通讯录,它并不具备保存功能,但是我们应该让它拥有一个初始化功能。


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


1.菜单的创建


首先要有一个一目了然的菜单。


要实现这么多的功能,我们首先想到了switch语句,用case表示不同的情况。


不过考虑到case 1,case 2这样的写法不容易读懂,代码的可维护性也不高。


我们在这里定义一个枚举类型。


enum Option
{
  EXIT,
  ADD,
  DEL,
  SEARCH,
  MODIFY,
  SHOW,
  SORT
};

如图所示,枚举类型在我们没有额外操作的情况下,里面的元素是从零开始。


这样一来,我们就可以从case 1这样的写法变成 case ADD,一目了然,一看就能看懂。


我们也需要打印选项,另外定义一个menu函数:


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");
}


2.主函数的创建


当要实现某一个函数功能时,我们直接在case语句后调用此函数便可。


int main()
{
  int input = 0;
  do
  {
  menu();
  printf("请选择:>");
  scanf("%d", &input);
  switch(input)
  {
  case ADD:
    break;
  case DEL:
    break;
  case SEARCH:
    break;
  case MODIFY:
    break;
  case SHOW:
    break;
  case SORT:
    break;
  case EXIT:
    printf("退出");
    break;
  default:
    break;
  }
  } while (input);
  return 0;
}


缺少的东西我们待会再补上。


3.定义联系人内容(结构体)


一个人的信息包括很多方面,要录入的话还是用结构体比较方便。


我们定义姓名,年龄,性别,电话和地址五个变量,并将其重命名为PeoInfo。


typedef struct PeoInfo
{
  char name[MAX_NAME];
  int age;
  char sex[MAX_SEX];
  char tele[MAX_TELE];
  char addr[MAX_ADDR];
}PeoInfo;


4.定义通讯录内容(结构体)


typedef struct Contact
{
  PeoInfo data[MAX];
  int sz;
}Contact;

我们要在什么位置插入一个元素呢?


我们知道,下标是从0开始的。


当我们插入一个数据时,我们实际上是插入在我们所定义的sz的位置。


举个例子。


当sz为5时,我们已经插入了下标为0,1,2,3,4的五个元素。


这时如果要插入新元素的话,就是在下标为5的地方插入,也就是插入到sz的位置。


但是我们并没有在sz的位置分配空间,所以我们在输入之后,需要将sz加一来分配空间。


void AddContact(Contact* pc)
{
  if (pc->sz == 100)
  {
  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);
  pc->sz++;
  printf("添加成功\n");
}


需要注意的是,当空间已满时,要返回,这时就不要往里面硬加了。


5.避免代码冗余而定义的查找函数


在接下来的修改或者删除函数里面,我们将会需要查找所要删除或者修改的人的名字这样的一个功能,为了避免冗余,我们不是在用到时再写一遍,而是定义一个函数,在用到它的时候直接调用,这就方便了许多。


int FindByName(Contact* pc, char name[])
{
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
  if (strcmp(pc->data[i].name, name) == 0)
  {
    return i;
    break;
  }
  }
  return -1;
}

当查找到要查找到名字时,返回这个位置的下标,没有找到则返回-1。


6.删除联系人信息


要删除一个人的信息,首先需要输入这个人的名字并查找它的位置,这时上面的代码就派上用场了。


我们先调用函数找到这个人的信息,然后调用它的返回值,也就是目标联系人的下标,并用这个下标来修改。


修改的方式是经典的后一个覆盖前一个,不过由于下标是元素个数再减一,为了避免溢出,我们需要是将sz减一。


void DelContact(Contact* pc)
{
  char name[MAX_NAME];
  if (pc->sz == 0)
  {
  printf("通讯录已满\n");
  return;
  }
  printf("输入要删除的人的名字:>");
  scanf("%s", name);
  int pos = FindByName(pc, name);
  if (pos == -1)
  {
  printf("要删除的人不存在\n");
  return;
  }
  for (int i = pos; i < pc->sz - 1; i++)
  {
  pc->data[i] = pc->data[i + 1];
  }
  pc->sz--;
  printf("删除成功\n");
}


同样的,在通讯录元素已满时,返回。


7.修改通讯录信息


首先查找,当查找到时,我们只需利用它的下标,来将此位置的信息进行重新录入即可。


void ModifyContact(Contact* pc)
{
  printf("输入要修改的人的名字:>");
  char name[12] = { 0 };
  scanf("%s", name);
  int pos = FindByName(pc, name);
  if (pos == -1)
  {
  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");
}


8.显示某一个人的信息


我们调用上面的查找函数 ,找到就用返回值下标打印出来就行。


int SearchContact(Contact* pc)
{
  char name[12] = { 0 };
  printf("请输入要查找的人的名字:>");
  scanf("%s", name);
  int pos = FindByName(pc, name);
  if (pos == -1)
  {
  printf("此人信息不存在\n");
  } 
  printf("此人信息如下:\n");
  printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");
  printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
  return pos;
}


9.整个通讯录的打印


我们调用sz,来将每一个人的信息打印出来。


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


10.将通讯录按名字排序


qsort函数可以很好的实现这个问题。


1.首元素地址base

我们要排序一组数据,首先我们需要找到这组数据在哪,因此我们直接将首元素的地址传给qsort函数来确定从哪开始排序。


2.元素个数num

我们知道了从哪开始,也要知道在哪结束才能确定一组需要排序的数据,但是我们不方便直接将结尾元素的地址传入函数,因此我们将需要排序的元素的个数传给qsort函数来确定一组数据。


3.元素大小size

我们知道qsort函数能排序任意数据类型的一组数据,因此我们用void*类型的指针来接收元素,但是我们知道void*类型的指针不能进行加减操作,也就无法移动,那么在函数内部我们究竟用什么类型的指针来操作变量呢?我们可以将void*类型的指针强制类型转换成char*类型的指针后来操作元素,因为char*类型的指针移动的单位字节长度是1个字节,我们只需要再知道我们需要操作的数据是几个字节就可以操作指针从一个元素移动到下一个元素,因此我们需要将元素大小传入qsort函数。


4.自定义比较函数compare


我们需要告诉qsort函数我们希望数据按照怎么的方式进行比较,比如对于几个字符串,我们可以比较字符串的大小。代码如下,我们想要传入什么类型的参数,就要将其强制类型转换成什么类型,并且返回一个int值。


int cmp_by_name(const void* e1, const void* e2)
{
  return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

然后就是qsort函数的调用了:


void SortContact(Contact* pc)
{
  qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}


11.全部代码


下面是全部的代码,定义两个源文件和一个头文件。


//contact.h部分
#pragma once
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
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 DelContact(Contact* pc);
void SortContact(Contact* pc);
int SearchContact(Contact* pc);
void ModifyContact(Contact* pc); 
void ShowContact(Contact* pc);
int FindByName(Contact* pc, char name[]);
//contact.c
#include"contact.h"
void InitContact(Contact* pc)
{
  pc->sz = 0;
  memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{
  if (pc->sz == 100)
  {
  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);
  pc->sz++;
  printf("添加成功\n");
}
void DelContact(Contact* pc)
{
  char name[MAX_NAME];
  if (pc->sz == 0)
  {
  printf("通讯录已满\n");
  return;
  }
  printf("输入要删除的人的名字:>");
  scanf("%s", name);
  int pos = FindByName(pc, name);
  if (pos == -1)
  {
  printf("要删除的人不存在\n");
  return;
  }
  for (int i = pos; i < pc->sz - 1; i++)
  {
  pc->data[i] = pc->data[i + 1];
  }
  pc->sz--;
  printf("删除成功\n");
}
int FindByName(Contact* pc, char name[])
{
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
  if (strcmp(pc->data[i].name, name) == 0)
  {
    return i;
    break;
  }
  }
  return -1;
}
int cmp_by_name(const void* e1, const void* e2)
{
  return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int SearchContact(Contact* pc)
{
  char name[12] = { 0 };
  printf("请输入要查找的人的名字:>");
  scanf("%s", name);
  int pos = FindByName(pc, name);
  if (pos == -1)
  {
  printf("此人信息不存在\n");
  } 
  printf("此人信息如下:\n");
  printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");
  printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);
  return pos;
}
void ShowContact(Contact* pc)
{
  int i = 0;
  printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");
  for (i = 0; i < pc->sz; i++)
  {
  printf("%-10s %-4d %-5s %-12s %-30s\n",  pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);
  }
}
void ModifyContact(Contact* pc)
{
  printf("输入要修改的人的名字:>");
  char name[12] = { 0 };
  scanf("%s", name);
  int pos = FindByName(pc, name);
  if (pos == -1)
  {
  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");
}
void SortContact(Contact* pc)
{
  qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}
//test.c
#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,
  ADD,
  DEL,
  SEARCH,
  MODIFY,
  SHOW,
  SORT
};
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 SEARCH:
    SearchContact(&con);
    break;
  case MODIFY:
    ModifyContact(&con);
    break;
  case SHOW:
    ShowContact(&con);
    break;
  case SORT:
    SortContact(&con);
    break;
  case EXIT:
    printf("退出");
    break;
  default:
    printf("输入错误\n");
    break;
  }
  } while (input);
  return 0;
}


这篇博客旨在总结我自己阶段性的学习,要是能帮助到大家,那可真是三生有幸!😀如果觉得我写的不错的话还请点个赞和关注哦~我会持续输出编程的知识的!🌞🌞🌞  

9af570028e3e74c7becb7537f8559c2e_74d1f3cf3dc740dd842ead5ddb8c378d.jpeg


相关文章
|
7月前
|
小程序 容器
日程安排小程序实战教程
日程安排小程序实战教程
|
2月前
|
前端开发 Java
表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
文章通过一个表白墙/留言墙的初级SpringBoot项目实例,详细讲解了如何进行前后端开发,包括定义前后端交互接口、创建SpringBoot项目、编写前端页面、后端代码逻辑及实体类封装的全过程。
106 3
表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
6月前
|
算法 Java 开发工具
合集:编程相关的网站及书籍
合集:编程相关的网站及书籍
|
7月前
|
小程序
招聘小程序搭建教程
招聘小程序搭建教程
微信小游戏无代码开发视频教程合集2
嗨!大家好,我是小蚂蚁。 有同学问了,有没有视频学习资料呀!俺就喜欢喜欢看视频学呢!这个咱也有。这篇给大家整理一下今年录制的几个视频教程的合集,建议收藏,假期安排时间,好好学习。悄悄的做个游戏,然后惊艳很多人。 强烈建议收藏,另外如果视频对你有帮助的话,欢迎点赞,分享鼓励小蚂蚁继续创作。
106 0
|
开发工具
微信小游戏无代码开发视频教程合集1
昨天给大家整理了一份图文的合集资料,有同学问了,有没有视频学习资料呀!俺就喜欢喜欢看视频学呢!这个咱也有。这篇给大家整理一下今年录制的几个视频教程的合集,建议收藏,假期安排时间,好好学习。悄悄的做个游戏,然后惊艳很多人。 强烈建议收藏,另外如果视频对你有帮助的话,欢迎点赞,分享鼓励小蚂蚁继续创作。
138 0
微信小游戏无代码开发视频教程合集3
嗨!大家好,我是小蚂蚁。 昨天给大家整理了一份图文教程的合集资料,有同学问了,有没有视频学习资料呀!俺就喜欢喜欢看视!这个咱也有。这篇给大家整理一下今年录制的几个视频的合集,建议收藏,假期安排时间,好好学习。悄悄的做个游戏,然后惊艳很多人。 这些视频都是免费的,强烈建议收藏,另外如果对你有帮助的话,欢迎点赞,分享鼓励小蚂蚁继续创作。
137 0
|
运维 小程序
全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序
全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序
141 1
全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序
|
存储 小程序 容器
日程安排小程序实战教程(上篇)
日程安排小程序实战教程(上篇)
日程安排小程序实战教程(上篇)
|
SQL 开发框架 小程序
小程序开发-第二章第二节小程序电影卡片模板-全栈工程师之路-中级篇
小程序开发-第二章第二节小程序电影卡片模板-全栈工程师之路-中级篇
111 0
小程序开发-第二章第二节小程序电影卡片模板-全栈工程师之路-中级篇