手把手教你写通讯录(含动态版) 1

简介: 手把手教你写通讯录(含动态版)

一、框架

实现通讯录之前,我们要想一下,我们这个通讯录需要有什么功能。从手机自带的通讯录借鉴,通讯录的功能首先要能存放联系人的各种信息如:姓名,性别,年龄,联系方式,地址等等。此次要在这个基础上实现对通讯录存放的联系人的删除,查找,修改等等。


思路有了,那么我们先来打一个框架    先用printf实现一个菜单    这个菜单可以帮助我们更方便地使用通讯录,可理解为指引功能。接着,功能的选择,我们可以用switch实现   其次,这个通讯录,我们想让它一直进行下去,直到使用者不想用了,所以写一个循环    综上所述,代码实现我们可以写成这般

#include<stdio.h>
void menu()
{
    printf("**********************************************\n");
    printf("**********************************************\n");
    printf("*******1.增加联系人******2.删除联系人*********\n");
    printf("*******3.修改联系人******4.查找联系人*********\n");
    printf("*******5.打印通讯录******0.退出通讯录*********\n");
    printf("**********************************************\n");
    printf("**********************************************\n");
}
int main()
{
  int choose = 0;
  do
  {
        menu();
    scanf("%d", &choose);
    switch (choose)
    {
    case 1:
      //调用增加联系人的函数
      break;
    case 2:
      //调用删除联系人的函数
      break;
    case 3:
      //调用修改联系人信息的函数
      break;
    case 4:
      //调用查找联系人的函数
      break;
        case 5:
             //调用打印通讯录的函数
            break;
    case 0:
      //调用退出函数
      break;
    default:
      printf("语法错误,请重新输入\n");
      break;
    }
  } while (1);
  //写成无限循环,只能通过输入0退出循环
}

功能还未具体实现,只是个框架,接着我们想一想,联系人不可能只有一个,会有很多个  那么我们应该要用数组存放,而联系人所包含的信息也很多   有姓名,年龄,性别,联系方式,地址等等,一个普通的数组根本放不下,而创建多个数组分别存放又显得非常繁琐。所以我们应该创建一个联系人结构体   这样就能创建出对应的结构体数组,即联系人数组,操作起来就会方便很多。

#include<stdio.h>
struct people
{
  char name[10];//姓名存放的是字符串,故用char
  int age;     //年龄存放的是一个数字,故用int
  char sex[5];//与名字的原因相同
  char tel[12];//与名字的原因相同
  char address[15];//与名字的原因相同
};

但是,如果未来我们想要一个能存放15个元素的名字,我们就得从头把10给改到尾,非常不方便,所以这里采用了宏定义的方式,定义了几个全局常量,这样便能实现牵一发而动全身

#include<stdio.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
struct people
{
  char name[name_max];//姓名存放的是字符串,故用char
  int age;     //年龄存放的是一个数字,故用int
  char sex[sex_max];//与名字的原因相同
  char tel[tel_max];//与名字的原因相同
  char address[address_max];//与名字的原因相同
};

继续思考,联系人的结构体被定义完了,我们接下来是不是就应该创建一个对应的结构体数组然后思考怎么对着这个结构体数组增删改差啊,差不多到这一步了。但我们的框架还差一点。一开始我们的通讯录肯定是一个联系人都没有的,我们要往里边存放联系人,而每当你存放一个联系人就会使对应的联系人数组里边的联系人增多一个,下次再存放联系人的时候,对应数组的下标肯定要往后波动一个,我们是否可以创建一个变量专门来存放存放了多少个联系人   一开始没有联系人,那么这个变量就为0,对应数组第一个元素的下标,进行存放操作时就直接取它,而存放完一个后,变量就+1,对应下一次要存放的目标下标。可是单独创建一个这样的变量,到时函数传参的时候一定会多一个步骤   似乎不是很方便,要是我们传一个变量,这个变量不单单是联系人数组,而是联系人数组和已存放联系人的数量就好了   所以我们可以再创建一个叫通讯录的结构体,把这两个都包含进去   如此便可以实现在调用函数传参时传一个就可以实现我们需要的功能。

#include<stdio.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 100//最多能存放100个联系人
typedef struct people
{
  char name[name_max];//姓名存放的是字符串,故用char
  int age;     //年龄存放的是一个数字,故用int
  char sex[sex_max];//与名字的原因相同
  char tel[tel_max];//与名字的原因相同
  char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
typedef struct contact
{
  peo data[people_max];
  //结构体成员名为data,它是可以存放100个struct people类型元素的数组
  int sz;
  //已经存放了几个联系人
}con;//别名,使用方便

框架到这里基本就打好了,接下来便是实现

二、实现

1.初始化通讯录

在做一切操作之前我们都应该先创建一个通讯录类型(我们之前创建的那个结合了成员数组和存放个数)的变量,然后对它初始化,可以把初始化通讯录这个功能单独分装成一个函数,我们这个函数的返回类型笔者给的是空类型的,因为我们只是用它来初始化通讯录,当然你想的话可以给它的返回类型设计成通讯录的指针变量,这样到时就可以实现返回通讯录的首地址,实现链式法则,进行操作。

void init_contact(con* c1)//传址调用,实现修改
{
  assert(c1);
  //断言,避免传空指针,使用这个函数需要引assert.h头文件
  memset(c1->data,0,sizeof(c1->data));
  //c1->data,指向的是数组名,代表着数组首元素的地址
  //sizeof(c1->data)代表计算整个数组所占的字节数
  //0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
  //memset位于stdlib.h头文件中
  c1->sz = 0;//把c1指向的sz初始化为0
}

2.增加联系人

初始化完联系人,我们的所有前置准备动作算是终于做完,接下来就讲一下如何添加联系人。其实很简单,下标有了(sz,也就是当前通讯录存放的联系人个数)自然就能找到被操作的目标。通过箭头访问结构体中的成员    再通过sz找到目标,找到目标之后,使用scanf对它们修改就行   要注意的一点就是,当我们增加联系人的时候,通讯录已经放满了很显然就不能再放了,再放就属于是越界访问了所以我们应该来个判断,当存放的联系人达到上限了就别存了。

void add(con*c1)
{
  assert(c1);//断言防止传空指针
  if (c1->sz == people_max)
  {
    printf("通讯录已满,存放失败\n");
    //存放失败就直接返回,由于是空类型,所以直接return
    return;
  }
  else//通讯录没满就往里存
  {
    printf("请输入联系人姓名\n");
    scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
    printf("请输入联系人年龄\n");
    scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
    printf("请输入联系人性别\n");
    scanf("%s", c1->data[c1->sz].sex);
    printf("请输入联系人联系方式\n");
    scanf("%s", c1->data[c1->sz].tel);
    printf("请输入联系人家庭住址\n");
    scanf("%s", c1->data[c1->sz].address);
    printf("添加成功\n");
    c1->sz += 1;//联系人增多一名
  }
}

写好的函数别忘了放在switch语句中调用

3.打印通讯录

实现增加联系人的功能之后你肯定会感觉怪怪的,我这添加了跟没添加一样,我又看不到,那么我们就一起来实现一个打印通讯录的功能,这样就能够看到我们添加进去的信息了,先思考返回类型,只是打印通讯录的内容,没有做别的操作,继续用空类型的,参数部分还是传通讯录结构体变量指针(有这个东西,通讯录的所有内容都可以找的出来),如此便已经可以实现我们的功能了,但我们可以设计的更完美一些,我们的目标只是打印通讯录的内容,并不会对内容进行修改,故我们可以在*号前加一个const修饰,这样通讯录结构体变量指针所指向的内容就不可能被修改了。

void print_contact(const con* c1)
{
  assert(c1);//断言避免传空指针
  int i = 0;
  if (c1->sz == 0)
  {
    printf("通讯录未存放联系人\n");
    return;
  }
  printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
  //根据自己的喜好对齐,使信息更加明了
  for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
  {
    printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
      c1->data[i].name,
      c1->data[i].age,
      c1->data[i].sex,
      c1->data[i].tel,
      c1->data[i].address);
  }
}

写好的函数别忘了放到switch语句中调用  

4.删除联系人

删除联系人,我们一样的返回类型一样采用空类型,参数一样是通讯录结构体变量的指针,但由于要修改,故不用const修饰。我们可以通过输入姓名的方式,再通过strcmp和循环,循环次数显然是当前有多少个联系人就循环几次,相当于是把通讯录遍历一遍来寻找是否存在目标人物,存在就把它对应的下标存储起来,不存在就直接返回就行。找到目标后,下一步就是删除,删除的话可以通过覆盖的方式来删除,比方说我要删除的这个联系人的下标为2即第三个元素,而我已经存放了5个成员,那么我们就将第四个元素覆盖到第三个元素上,将第五个元素覆盖到第四个元素上,再对sz--就可以实现删除,理论存在,开始实践

void con_del(con* c1)//删除联系人
{
  assert(c1);
  if (c1->sz == 0)
  {
    printf("通讯录为空,操作失败\n");
    return;
  }
  char a1[name_max] = { 0 };//初始化
  printf("请输入你要删除联系人的姓名\n");
  scanf("%s", &a1);
  int i = 0; int flaw = 0;
  for (i = 0; i < c1->sz; i++)
  {
    if (strcmp(a1, c1->data[i].name) == 0)
    {
      flaw = 1;//用来分析是否查找到目标
      break;//找到下标,退出循环
    }
  }
  if (flaw == 0)
  {
    printf("查无此人,删除失败\n");
    return;
  }
  for (; i < c1->sz-1; i++)
  //为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
    //因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
  //且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
  {
    c1->data[i] = c1->data[i+1];
  }
  printf("删除成功\n");
  c1->sz--;
}

实现完这个部分之后,笔者又觉得这个查找目标下标的功能啊,它很方便,很有用啊,到时我们做查找并打印功能时就可以用到,于是我们把这部分功能分装成一个函数,让它通过姓名的方式为我们查找目标的下标

int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
  char a1[name_max] = { 0 };//初始化
  scanf("%s", &a1);
  int i = 0;
  for (i = 0; i < c1->sz; i++)
  {
    if (strcmp(a1, c1->data[i].name) == 0)
    {
      return i;//找到目标,直接返回下标,注意下标可能为0
    }
  }
  return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{ 
    assert(c1);
  if (c1->sz == 0)
  {
    printf("通讯录为空,操作失败\n");
    return;
  }
  printf("请输入你要删除的联系人的姓名\n");
  int i = find(c1);
  if (i!=-1)
  {//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
    for (; i < c1->sz - 1; i++)
      //为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
      //因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
      //且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
    {
      c1->data[i] = c1->data[i + 1];
    }
    printf("删除成功\n");
    c1->sz--;
  }
  else
  {
    printf("查无此人,操作失败\n");
    return;
  }
}


相关文章
【C进阶】通讯录的实现(静态+动态)(上)
【C进阶】通讯录的实现(静态+动态)(上)
|
6月前
|
C++
【C/C++基础实战】:用C++实现通讯录管理系统——含完整源码
【C/C++基础实战】:用C++实现通讯录管理系统——含完整源码
|
7月前
|
存储
手把手教你实现通讯录
手把手教你实现通讯录
56 0
|
7月前
动态通讯录(并不难都能拿下)
动态通讯录(并不难都能拿下)
C进阶:通讯录(动态版本 + 文件操作)附源码(下)
C进阶:通讯录(动态版本 + 文件操作)附源码(下)
75 0
|
存储 搜索推荐
C进阶:通讯录(动态版本 + 文件操作)附源码(上)
C进阶:通讯录(动态版本 + 文件操作)附源码
47 0
【C进阶】通讯录的实现(静态+动态)(中)
【C进阶】通讯录的实现(静态+动态)(中)
【C进阶】通讯录的实现(静态+动态)(下)
【C进阶】通讯录的实现(静态+动态)(下)
|
小程序 数据可视化 数据库
云开发(微信-小程序)笔记(十七)---- cms(内容管理)及案例
云开发(微信-小程序)笔记(十七)---- cms(内容管理)及案例
541 0