目录
[查找函数 contact.c](#查找函数 contact.c)
[删除 contact.c](#删除 contact.c)
实现以下内容:
- 人的信息:姓名+年龄+性别+地址+电话
- 通讯录中可以存放100个人的信息
- 功能:
- 增加联系人
- 删除指定联系人
- 查找指定联系人的信息
- 修改指定联系人的信息
- 显示所有联系人的信息
- 排序(名字、年龄)
通过3个文件实现:
-
test.c ------ 测试通讯录
-
contact.c ------ 通讯录的实现
-
contact.h ------ 函数的声明、类型的定义和声明
一.代码框架
test.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#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");
}
int main()
{
int input = 0;
//创建通讯录
PeoInfo data[100];
do
{
menu();
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 0:
printf("退出通讯录");
break;
default:
printf("输入错误\n");
break;
}
} while (input);
return 0;
}
contact.h
cpp
#pragma once
#include <stdio.h>
//人的信息
typedef struct PeoInfo
{
char name[20];
int age;
char sex[5];
char addr[30];// 地址
char tele[12];// 电话11个字符,还要给 \0 留位置
}PeoInfo;// 类型复杂,重命名
优化
疑问:添加联系人进去,放哪个下标位置?
答:必须知道已经放进去了几个,所以创建变量 sz
test.c
cpp
int main()
{
int input = 0;
//创建通讯录
PeoInfo data[100];
int sz;
......
2个变量太分散?再分装到结构体
contact.h
cpp
#pragma once
#include <stdio.h>
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
char tele[TELE_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo data[MAX];//存放人的信息
int sz;//当前已经存入联系人的个数
}Contact; // 重命名
test.c
cpp
int main()
{
int input = 0;
//创建通讯录
Contact con;
......
二.初始化通讯录
不这样:Contact con = { 0 } ; 太粗暴。
要细致化,模块化。初始化时若自己有想法,按自己的逻辑,通过分装函数来实现。
test.c
cpp
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);
......
contact.h
cpp
#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
char tele[TELE_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo data[100];//存放人的信息
int sz;//当前已经存入联系人的个数
}Contact;
//初始化通讯录 -- 函数声明
void InitContact(Contact* pc);
这里涉及到结构体传参
我们要传地址,每个人的信息就很大,而且有100个人。传值的话要拷贝,浪费栈帧空间,效率低下。
Contact con ; con 的类型 Contact 。地址传给 pc ,所以 pc 的类型为Contact *
contact.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
memset:内存设置函数,以字节为单位来设置内存中的数据。

三个参数依次是:要设置的起始地址,要设置的值,设置多少字节的值
疑问:pc->data 这不是数组名**(传过来的,比如数组传参数组接收)吗?拿 sizeof 传参算的结果是4
答:InitContact (&con) ; 传的是结构体的地址,结构体里面包含了一个数组。(传的不是数组本身)**
数组传参的时候传过去数组名是首元素地址,那时候会降级为指针。
这里传的是结构体的地址,进来之后通过结构体的地址找到它所指向的对象里面的 data 数组。
并且把数组名单独放在 sizeof 内部,属于2个特殊情况之一。这时可以通过这样的方式求整个数组的大小,单位:字节
三、功能实现
1.增加联系人
涉及修改通讯录,依然传地址。
先判断联系人是否满了。
放进去,sz++。放到那呢?

没有联系人,增加的放到下标为0;有1个,增加的风道下标为1;放到下标为 sz 的位置
test.c
cpp
case 1:
AddContact(&con);
break;
contact.h
cpp
//增加联系人 -- 函数声明
void AddContact(Contact* pc);
contact.h
cpp
void AddContact(Contact* pc)
{
assert(pc);
if (pc->sz == MAX)
{
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].addr);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
**解析:**放到 pc 所指向的结构体通讯录里面 data 数组里面,下标为 sz 的位置。
**sz 怎么找?**pc 所指向的结构体通讯录里面的 sz
**. name?**data[ ] 是data 数组里面的一个元素,是 PeoInfo 的数据。放名字的话,放到 name 里面去
**不 &?**name 本来就是数组,数组名本来就是地址,所以不用 &。
但 age 是变量,要 &
5.显示通讯录中的信息
contact.c
cpp
void ShowContact(const Contact* pc)
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%s\t%d\t%s\t%s\t%s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
优化:
%20s:最多打印20个字符 一个汉字占2个字节
右对齐:
cpp
printf("%20s\t%4d\t%5s\t%20s\t%12s\n", ......

左对齐:
cpp
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", ......

加上列名 contact.c
cpp
void ShowContact(const Contact* pc)
{
assert(pc);
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}

2.删除指定联系人
先判断通讯录是否为空
删除,找 到谁,去删除; 查找,找 到谁,去查找; 修改,找 到谁,去修改
这些函数都会用到查找。把查找提取出来,分装成函数。遍例查找
怎么删除?
(1)后面的往前覆盖,sz--。 挪动的数据多,考虑效率能不能接受。(我们演示这个)
(2)最后一个覆盖,sz--。 考虑顺序变动能不能接受。
查找函数 contact.c
cpp
int FindByName(const Contact* pc, char name[])
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
删除 contact.c
cpp
void DelContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
//查找
printf("请输入要删除的人的名字:>");
scanf("%s", name);
int ret = FindByName(pc, name);
if (-1 == ret)
{
printf("要删除的人不存在\n");
return;
}
int i = 0;
//删除
for (i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
效果:
疑问:最后一个联系人还在呀!不管了吗?
答:sz-- 后,访问不到了。在不在效果一样。
3.查找指定联系人
contact.c
cpp
void SearchContact(const Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要查找人的名字:>");
scanf("%s", name);
int pos = FindByName(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
//打印信息
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
效果:
4.修改指定联系人
contact.c
cpp
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要修改人的名字:");
scanf("%s", name);
int pos = FindByName(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].addr);
printf("请输入电话:");
scanf("%s", pc->data[pos].tele);
printf("修改完成\n");
}
效果:
四.整体代码
test.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#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");
}
int main()
{
int input = 0;
//创建通讯录
Contact con;
//初始化通讯录
InitContact(&con);
do
{
menu();
printf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
SearchContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 6:
break;
case 0:
printf("退出通讯录");
break;
default:
printf("输入错误\n");
break;
}
} while (input);
return 0;
}
contact.h
cpp
#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12
//人的信息
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
char tele[TELE_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo data[MAX];//存放人的信息
int sz;//当前已经存入联系人的个数
}Contact;
//初始化通讯录 -- 函数声明
void InitContact(Contact* pc);
//增加联系人 -- 函数声明
void AddContact(Contact* pc);
//显示通讯录中的信息 -- 函数声明
void ShowContact(const Contact* pc);
//删除指定联系人
void DelContact(Contact* pc);
//查找指定联系人
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
contact.c
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{
assert(pc);
if (pc->sz == MAX)
{
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].addr);
printf("请输入电话:");
scanf("%s", pc->data[pc->sz].tele);
pc->sz++;
}
void ShowContact(const Contact* pc)
{
assert(pc);
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
int i = 0;
for (i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].tele);
}
}
int FindByName(const Contact* pc, char name[])
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void DelContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
//查找
printf("请输入要删除的人的名字:>");
scanf("%s", name);
int ret = FindByName(pc, name);
if (-1 == ret)
{
printf("要删除的人不存在\n");
return;
}
int i = 0;
//删除
for (i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
void SearchContact(const Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要查找人的名字:>");
scanf("%s", name);
int pos = FindByName(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
//打印信息
printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].tele);
}
void ModifyContact(Contact* pc)
{
assert(pc);
char name[NAME_MAX] = { 0 };
printf("请输入要修改人的名字:");
scanf("%s", name);
int pos = FindByName(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].addr);
printf("请输入电话:");
scanf("%s", pc->data[pos].tele);
printf("修改完成\n");
}
通讯录排序见小编下一篇文章
这就是我们当前写的通讯录,还是比较简单,粗糙的。
信息并没有存储起来。下一次运行时,上一次存的信息全都没了。
因为程序运行时,输入的数据是放到内存当中的。
怎么办呢?后期小编再给大家讲