1.实现功能
本文将采用C语言来实现一个简单的通讯录,要求功能如下
代码语言:javascript
复制
//实现一个通讯录
//1.可以保存100个人的信息
//2.增加人的信息
//3.删除指定联系人的信息
//4.查找
//5.修改
//6.排序
//7.显示所有联系人
下面我们来一步步实现
2.实现细节与具体思路
1.主程序设计
首先,设计一个主程序来对于通讯录进行一个整体的设计,它的功能我们用函数来进行包装,这样可以增加代码的可读性。
代码语言:javascript
复制
#include"contact.h"
void menu()
{
printf("*************************************\n");
printf("********1.add*************2.del*******\n");
printf("*************************************\n");
printf("*************************************\n");
printf("******3.search**********4.mobify*********\n");
printf("*************************************\n");
printf("******5.show***********6sort******\n");
printf("*0.exit******************************\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);
ShowContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
这里我们对于通讯录的功能用一个枚举常量来包装(枚举常量默认从0开始),虽然也可以直接数字来代替进入switch,但这样可以增加代码的阅读性,方便我们进行接下来的操作。这里我们用一个头文件contact.h来包装我们所需要的头文件以及函数的声明,使代码看上去更加整洁.
头文件contact.h
代码语言:javascript
复制
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define DATA_MAX 100
//类型声明
typedef struct PeopleINfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char addr[ADDR_MAX];
}PeoInfo;
typedef struct Contact
{
//创建通讯录
PeoInfo data[DATA_MAX];//可以存放100个人的信息
//一开始里面没有信息
int sz;//记录已经存放的信息个数
}Contact;
//初始化
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//展示所有联系人
void ShowContact(const Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(Contact* pc);
//修改指定联系人的相关信息
void ModifyContact(Contact* pc);
//排序
void SortContact(Contact* pc);
里面是一些函数的声明以及一些变量的初始化.下面会讲到.
功能设计
1.创建一个通讯录并进行初始化
首先我们要明确,一个通讯录里放的应该是什么样的信息,既然是通讯录,肯定得有名字和电话号码,除此之外,也可以有性别,住址或者年龄,为了简单,我们就设计这些元素.我们用一个结构体来存放这些变量,命名为PeopleInfo类型代表联系人的信息.接下来创建通讯录,通讯录里要有联系人的信息,同时还得记录通讯录里人的个数,这里我们也可以用一个结构体Contact来存放它们.
代码语言:javascript
复制
typedef struct Contact
{
//创建通讯录
PeoInfo data[DATA_MAX];//可以存放100个人的信息
//一开始里面没有信息
int sz;//记录已经存放的信息个数
}Contact;
下面进行初始化
代码语言:javascript
复制
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
assert函数判断pc指向的地址是否为空,memset函数用来给pc里的data数组进行初始化.
这里要注意的是,创建通讯录变量一定是在主函数里创建的(在初始化函数里创建,函数结束后会销毁) ,同时,给结构体传参时,传值和传址的效果是一样的,只是传址的话不用开辟空间,效率更高.
2.增加功能
代码语言:javascript
复制
void AddContact(Contact* pc)
{
assert(pc);
//判断通讯录满没满
if (pc->sz == DATA_MAX)
{
printf("通讯录已满,无法增加\n");
}
//增加信息
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");
}
每设计一个函数功能时,我们都可以用assert判断一下,这样运行会更加安全.
3.删除功能
这里要注意的是,我们要求的是删除指定联系人而不是全删,所以必须要先找到相应联系人才能进行删除,同时后面的修改功能也得找到相应联系人才能删除,所以我们可以定义一个寻找函数进行调用
代码语言:javascript
复制
FindByName(Contact* pc, char name[])//name字符串来存储我们输入的那个需要的名字
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->data[i].name)==0)
{
return i;//找到了
}
}
return -1;//没找到
}
接下来是删除,删除的算法有说法,我们知道,数组在内存里是连续存储的,我们可以利用它的下标,用它的下一个值赋给它来达到删除的效果,这里要循环赋值,否则会出现两个一样值,从需要删除的值的下标到最后,删完后,别忘了给sz(数据的个数)-1.
代码语言:javascript
复制
void DelContact(Contact* pc)
{
char name[NAME_MAX];
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空,无需删除\n");
return;
}
//找到指定联系人
printf("请输入你要删除的联系人名字:\n");
scanf("%s", name);
int ret=FindByName(pc, name);
if (ret == -1)
{
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");
}
4. 查找功能
查找功能的实现,不仅要找到还要输出出来,这里刚刚创建的查找函数可以直接在这里使用
代码语言:javascript
复制
void SearchContact(Contact* pc)
{
char name[NAME_MAX];
assert(pc);
printf("请输入要查找人的名字:");
scanf("%s", name);
int ret = FindByName(pc, name);
if (ret == -1)
{
printf("该联系人不存在\n");
return;
}
//找到了,显示出来
printf("%-20s %-5s %-5s %-12s %-30s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s %-5d %-5s %-12s %-30s\n",
pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}
为了方便查看,可以输出对应的标识名,注意格式对齐就行.
5.显示所有联系人
代码语言:javascript
复制
void ShowContact(const Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空\n");
return;
}
int i = 0;
//进行标题修饰
printf("%-20s %-5s %-5s %-12s %-30s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
//打印每个人的信息
printf("%-20s %-5d %-5s %-12s %-30s\n",
pc->data[i].name, pc->data[i].age, pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);
}
这个功能只是简单的循环数组打印出来,没什么好说的,唯一要注意的是如果为空的话就不需要打印了,这里判断一下
6.修改功能
代码语言:javascript
复制
void ModifyContact(Contact* pc)
{
char name[NAME_MAX];
assert(pc);
printf("请输入要修改人的名字:");
scanf("%s", name);
int ret = FindByName(pc, name);
if (ret == -1)
{
printf("该联系人不存在\n");
return;
}
//修改
/*printf("请输入名字:");
scanf("%s", pc->data[ret].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[ret].age));
printf("请输入性别:");
scanf("%s", pc->data[ret].sex);
printf("请输入号码:");
scanf("%s", pc->data[ret].tele);
printf("请输入地址:");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");*/
//修改单独一个
printf("请输入你要修改的类型:1.名字 2.年龄 3.性别 4.电话号码 5.住址 0.取消修改\n");
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入名字:");
scanf("%s", pc->data[ret].name);
printf("修改成功\n");
break;
case 2:
printf("请输入年龄:");
scanf("%d", &(pc->data[ret].age));
printf("修改成功\n");
break;
case 3:
printf("请输入性别:");
scanf("%s", pc->data[ret].sex);
printf("修改成功\n");
break;
case 4:
printf("请输入号码:");
scanf("%s", pc->data[ret].tele);
printf("修改成功\n");
break;
case 5:
printf("请输入地址:");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
break;
case 0:
printf("成功取消\n");
break;
}
}
整体思路就是:先找到相应联系人,找到后要么全部修改,要么输入相应的数字修改相应的信息
7.排序
这里先介绍一下qsort函数的用法
它的功能是对所传入的元素进行排序,要传入的参数是需要比较元素的起始地址,比较的个数,所比较元素的单个大小,以及一个比较函数.
比较函数这里有说法,我们来看看
大概意思是,如果p1的值大于p2就返回大于0的值,反之返回小于0的值,相等则返回0,通常的通讯录是按照名字排序(字典序),所以这里要比较字符串的大小,不能直接相减,得用strcmp函数,这里用它的好处就是,它的返回值和这个比较函数的规则是一样的,
比较函数
代码语言:javascript
复制
int cmp_by_name(const void* p1, const void* p2)
{
return strcmp(((Contact*)p1)->data->name, ((Contact*)p2)->data->name);
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);
printf("排序成功!\n");
}
cmp接收的参数必须是void*类型,所以进入函数要记住强转一下.
3.完整代码
代码语言:javascript
复制
#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 == DATA_MAX)
{
printf("通讯录已满,无法增加\n");
}
//增加信息
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 ShowContact(const Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空\n");
return;
}
int i = 0;
//进行标题修饰
printf("%-20s %-5s %-5s %-12s %-30s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
//打印每个人的信息
printf("%-20s %-5d %-5s %-12s %-30s\n",
pc->data[i].name, pc->data[i].age, pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);
}
}
FindByName(Contact* pc, char name[])
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->data[i].name)==0)
{
return i;//找到了
}
}
return -1;//没找到
}
void DelContact(Contact* pc)
{
char name[NAME_MAX];
assert(pc);
if (pc->sz == 0)
{
printf("通讯录为空,无需删除\n");
return;
}
//找到指定联系人
printf("请输入你要删除的联系人名字:\n");
scanf("%s", name);
int ret=FindByName(pc, name);
if (ret == -1)
{
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(Contact* pc)
{
char name[NAME_MAX];
assert(pc);
printf("请输入要查找人的名字:");
scanf("%s", name);
int ret = FindByName(pc, name);
if (ret == -1)
{
printf("该联系人不存在\n");
return;
}
//找到了,显示出来
printf("%-20s %-5s %-5s %-12s %-30s\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s %-5d %-5s %-12s %-30s\n",
pc->data[ret].name, pc->data[ret].age, pc->data[ret].sex, pc->data[ret].tele, pc->data[ret].addr);
}
void ModifyContact(Contact* pc)
{
char name[NAME_MAX];
assert(pc);
printf("请输入要修改人的名字:");
scanf("%s", name);
int ret = FindByName(pc, name);
if (ret == -1)
{
printf("该联系人不存在\n");
return;
}
//修改
/*printf("请输入名字:");
scanf("%s", pc->data[ret].name);
printf("请输入年龄:");
scanf("%d", &(pc->data[ret].age));
printf("请输入性别:");
scanf("%s", pc->data[ret].sex);
printf("请输入号码:");
scanf("%s", pc->data[ret].tele);
printf("请输入地址:");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");*/
//修改单独一个
printf("请输入你要修改的类型:1.名字 2.年龄 3.性别 4.电话号码 5.住址 0.取消修改\n");
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入名字:");
scanf("%s", pc->data[ret].name);
printf("修改成功\n");
break;
case 2:
printf("请输入年龄:");
scanf("%d", &(pc->data[ret].age));
printf("修改成功\n");
break;
case 3:
printf("请输入性别:");
scanf("%s", pc->data[ret].sex);
printf("修改成功\n");
break;
case 4:
printf("请输入号码:");
scanf("%s", pc->data[ret].tele);
printf("修改成功\n");
break;
case 5:
printf("请输入地址:");
scanf("%s", pc->data[ret].addr);
printf("修改成功\n");
break;
case 0:
printf("成功取消\n");
break;
}
}
int cmp_by_name(const void* p1, const void* p2)
{
return strcmp(((Contact*)p1)->data->name, ((Contact*)p2)->data->name);
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_by_name);
printf("排序成功!\n");
}