本通讯录的最终目标:
1. 可以存放1000个人的信息
2. 人的信息:名字,性别,年龄,电话,住址
3. 增加联系人
4. 删除联系人
5. 查找联系人
6. 修改联系人
7. 排序(名字 / 年龄)
8. 清除所有联系人
前面的三子棋和扫雷都只是比较注重函数的声明和调用这个模块,本次通讯录总共有三个版本,虽然这个是初级版本,但是是之后高级通讯录的重要基础,还有就是这个通讯录相较于三子棋和扫雷除函数调用外,还多出了结构体、指针、数组等一系列之前工程所不具备的。
本次程序是在VS环境下实现的,分为三个文件(一个头文件contact.h和两个源文件test.c&contact.c),下文主要介绍test.c文件里的内容(也就是整体框架)和contact.c文件里的实现方法。
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
enum Oprion
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
SHOW,
CLEAR
};
void menu()
{
printf("***********************************\n");
printf("******** 1. add 2. del ******\n");
printf("******** 3. search 4. modify******\n");
printf("******** 5. sort 6. show ******\n");
printf("******** 7. clear 0. exit ******\n");
printf("***********************************\n");
}
int main()
{
int input = 0;
Contact con = { 0 }; //通讯录
//初始化通讯录
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 SORT:
SortContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case CLEAR:
ClearContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
这里非常灵活地使用枚举类型,将0~7的值对应的操作用枚举封装起来,然后下面case的时候就不用再去记那些操作对应的数值是几,然后通讯录的增删查改、排序等操作写在函数里,再将通讯录传过去,其他的代码和之前的三子棋、扫雷的代码基本类似,这里不做过多描述。
contact.c(详细解释在下面代码注释中):
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
void InitContact(Contact* con)
{
assert(con);
memset(con->data, 0, sizeof(con->data));
con->sz = 0;
}
//将通讯录结构体中所有成员都初始化,结构体的定义和声明放在contact.h
void AddContact(Contact* con)
{
assert(con);
if (con->sz == MAX)
{
printf("通讯录满了,无法添加\n");
return;
}
//输入联系人
printf("请输入名字:>");
scanf("%s", con->data[con->sz].name);
printf("请输入性别:>");
scanf("%s", con->data[con->sz].sex);
printf("请输入年龄:>");
scanf("%d", &(con->data[con->sz].age));
printf("请输入电话号码:>");
scanf("%s", con->data[con->sz].tele);
printf("请输入住址:>");
scanf("%s", con->data[con->sz].addr);
con->sz++;
printf("添加联系人成功!\n");
}
//通讯录中联系人的添加,用sz来判断已经存入联系人的数量,data来存放联系人的各种数据
int FindContact(const Contact* con, char name[])
{
assert(con && name);
int i = 0;
for (i = 0; i < con->sz; i++)
{
if (strcmp(con->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
//通讯录很多功能都同时会用到这个功能--找联系人名字,所以单独封装成一个函数,找到则返回i(也就是通讯录中的这个人所排的位置),找不到则返回-1
void ShowContact(const Contact* con)
{
assert(con);
printf("%-10s\t%-5s\t%-5s\t%-13s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
int i = 0;
for (i = 0; i < con->sz; i++)
{
printf("%-10s\t%-5s\t%-5d\t%-13s\t%-20s\n",
con->data[i].name, con->data[i].sex, con->data[i].age, con->data[i].tele, con->data[i].addr);
}
}
//将已有的通讯录中的全部内容打印出来,其中那些负号是打印格式,表示的是左对齐
void SearchContact(const Contact* con)
{
assert(con);
char searchname[NAME_MAX] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法查找\n");
return;
}
printf("请输入查找人的名字:>");
scanf("%s", searchname);
int ret = FindContact(con, searchname);
if (ret == -1)
{
printf("未能找到该联系人\n");
return;
}
else
{
printf("找到了:>\n");
printf("%-10s\t%-5s\t%-5s\t%-13s\t%-20s\n", "名字", "性别", "年龄", "电话", "地址");
printf("%-10s\t%-5s\t%-5d\t%-13s\t%-20s\n",
con->data[ret].name, con->data[ret].sex, con->data[ret].age, con->data[ret].tele, con->data[ret].addr);
}
}
//查找联系人并输出其全部信息,这里就要调用前面已经写好的找联系人名字函数了
void DelContact(Contact* con)
{
assert(con);
char delname[NAME_MAX] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
printf("请输入删除人名字:>");
scanf("%s", delname);
int ret = FindContact(con, delname);
if (ret == -1)
{
printf("通讯录中暂无此人,无法删除\n");
return;
}
else
{
int i = 0;
for (i = ret; i < con->sz - 1; i++)
{
con->data[i] = con->data[i + 1];
}
con->sz--;
printf("删除联系人成功!\n");
}
}
//删除通讯录中指定的联系人,这里同样要用到前面的找联系人名字函数,这里还有一点要注意的是,该函数中的删除并不能做到真正的删除,这里代码的意思其实是将后面联系人的信息覆盖到上一个人的信息位置,再把sz--,从而实现删除功能
void ModifyContact(Contact* con)
{
assert(con);
char modifyname[NAME_MAX] = { 0 };
char mod[5] = { 0 };
char modform[50] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法修改\n");
return;
}
printf("请输入修改人的名字:>");
scanf("%s", modifyname);
int ret = FindContact(con, modifyname);
if (ret == -1)
{
printf("通讯录中暂无此人,无法修改\n");
return;
}
else
{
printf("请选择修改的选项(名字/性别/年龄/电话/地址):>");
scanf("%s", mod);
if (strcmp(mod, "名字") == 0)
{
printf("请输入修改后的名字:>");
scanf("%s", modform);
strcpy(con->data[ret].name, modform);
printf("修改名字成功!\n");
}
else if (strcmp(mod, "性别") == 0)
{
printf("请输入修改后的性别:>");
scanf("%s", modform);
strcpy(con->data[ret].sex, modform);
printf("修改性别成功!\n");
}
else if (strcmp(mod, "电话") == 0)
{
printf("请输入修改后的电话:>");
scanf("%s", modform);
strcpy(con->data[ret].tele, modform);
printf("修改电话成功!\n");
}
else if (strcmp(mod, "地址") == 0)
{
printf("请输入修改后的地址:>");
scanf("%s", modform);
strcpy(con->data[ret].addr, modform);
printf("修改地址成功!\n");
}
else if(strcmp(mod, "年龄") == 0)
{
printf("请输入修改后的年龄:>");
scanf("%s", modform);
int newage = atoi(modform);
con->data[ret].age = newage;
printf("修改年龄成功!\n");
}
else
{
printf("选择错误\n");
}
}
}
//修改联系人的信息,这里同样用到了找联系人名字函数,然后再选择要修改的项。其中有一个新的知识点--atoi函数,其功能是可以将字符串转换成对应的数字,刚好年龄部分需要的就是整型,但是我们在前面统一输入的又只能是字符串,所以就使用到了atoi函数,具体的模拟实现atoi函数在本篇文章中不讲,放在下一篇通讯录的优化中讲,下一篇通讯录的优化是对内存进行优化,不一次性直接开辟1000个内存,而是改为动态内存,可以下来先思考一下是如何实现的
int cmp_people_by_name(const void* e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int cmp_people_by_age1(const void* e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
int cmp_people_by_age2(const void* e1, const void* e2)
{
return ((PeoInfo*)e2)->age - ((PeoInfo*)e1)->age;
}
void SortContact(Contact* con)
{
assert(con);
char sort[5] = { 0 };
char sortway[5] = { 0 };
if (con->sz == 0)
{
printf("通讯录为空,无法排序\n");
return;
}
else
{
printf("请选择排序方式(名字/年龄):>");
scanf("%s", sort);
if (strcmp(sort, "名字") == 0)
{
qsort(con->data, con->sz, sizeof(con->data[0]), cmp_people_by_name);
printf("排序成功!\n");
}
else if (strcmp(sort, "年龄") == 0)
{
printf("请选择递增or递减:>");
scanf("%s", sortway);
if (strcmp(sortway, "递增") == 0)
{
qsort(con->data, con->sz, sizeof(con->data[0]), cmp_people_by_age1);
printf("排序成功!\n");
}
else if (strcmp(sortway, "递减") == 0)
{
qsort(con->data, con->sz, sizeof(con->data[0]), cmp_people_by_age2);
printf("排序成功!\n");
}
else
{
printf("选择错误\n");
}
}
else
{
printf("选择错误\n");
}
}
}
//这里我分为两种排序方式:按姓名&按年龄(年龄又分为升序和降序两种排序方法),他们都是使用qsort进行快速排序的,关于qsort快速排序问题,之前有专门写一篇文章详细说明,可以回去重新复习~
void ClearContact(Contact* con)
{
assert(con);
if (con->sz == 0)
{
printf("通讯录为空,无需清除\n");
return;
}
else
{
memset(con->data, 0, sizeof(con->data));
con->sz = 0;
printf("清除所有联系人成功!\n");
}
}
//清楚通讯录中所有联系人信息,这里其实也是类似于讲通讯录初始化
contact.h:
#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#define MAX 1000
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 15
#define ADDR_MAX 30
typedef struct PeoInfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tele[TELE_MAX];
char addr[ADDR_MAX];
}PeoInfo;
//通讯录的结构体
typedef struct Contact
{
PeoInfo data[MAX]; //存放数据
int sz; //通讯录中有效信息个数
}Contact;
//初始化通讯录
void InitContact(Contact* con);
//增添联系人
void AddContact(Contact* con);
//删除联系人
void DelContact(Contact* con);
//查找联系人
void SearchContact(const Contact* con);
//修改联系人
void ModifyContact(Contact* con);
//排序联系人
void SortContact(Contact* con);
//打印联系人
void ShowContact(const Contact* con);
//清除所有联系人
void ClearContact(Contact* con);
这里这个头文件的作用是:1. 定义全局变量;2. 引头文件;3. 定义和声明结构体;4. 函数声明
整个初级通讯录的完整代码放在这里面,有兴趣可以看看:
蔡欣致/C - C++ 项目 - Gitee.comhttps://gitee.com/zkcxz/c-----c---project/tree/master/contact