因为和前面的小游戏实现方式是一个套路,所以分模块(声明模块、函数实现模块、测试模块)部分不再重复,感兴趣请点击->三子棋小游戏实现
下面开始这次项目的实现:
简单通讯录实现
一、大概流程框架实现
老规矩!首先是要把大的运行框架做出来,这里直接把测试和声明的代码放在一个内容。(为了方便讲解,说明部分都放到了注释中)
1、声明(contact.h)
//实现用到的库函数
#include <stdio.h>
#include <string.h>
//联系人的一些属性,需要给定一定大小的空间,这里使用#define定义的常量,方便后面使用
#define NAME 8
#define SEX 5
#define PHONE 12
#define ADDR 20
#define NUM 100
//定义的每个联系人结构体,包括五个属性(姓名,性别,年龄,电话,地址)
//定义完之后通过typedef重定义为people,方便后面调用
typedef struct people
{
char name[NAME];
char sex[SEX];
int age;
char phone[PHONE];
char addr[ADDR];
}people;
//定义的所有联系人(即通讯录)结构体,包括两个属性(联系人,数量)
//定义完之后通过typedef重定义为contact,方便后面调用
typedef struct contact
{
people data[NUM];
int num;
}contact;
//该项目实现的具体功能
//包括对联系人的增加、删除、查找、修改、显示所有联系人、排序、清空联系人列表
void add(contact* c);
void show(contact* c);
void del(contact* c);
void search(contact* c);
void modify(contact* c);
void clear(contact* c);
void sort(contact* c);
2、测试(test.c)
#define _CRT_SECURE_NO_WARNINGS
//只要包含自定义头文件就可以使用所有东西啦
#include "contact.h"
//这里定义一个menu函数,方便用户做选择
void menu()
{
printf(" ___________________________________\n");
printf("| |\n");
printf("| 1.add 2.del 3.search |\n");
printf("| 4.modify 5.sort 6.show |\n");
printf("| 7.claer 0.exit |\n");
printf("|___________________________________|\n");
printf("\n");
return;
}
//因为到这里已经基本掌握了枚举常量
//另外定义枚举常量之后,也增加了代码可读性,所以最好用一下,嘻嘻
enum menu
{
ADD = 1,
DEL = 2,
SEARCH = 3,
MODIFY = 4,
SORT = 5,
SHOW = 6,
CLEAR = 7,
EXIT = 0
};
int main()
{
//对需要用到的变量进行定义和初始化,这里contact是前面重定义之后结构体
int input = 0;
contact c = { 0 };
do
{
menu();
printf("请输入你的选择:");
scanf("%d", &input);
printf("\n");
//通过switch语句实现选择具体功能的逻辑
switch (input)
{
case ADD:
add(&c);
break;
case DEL:
del(&c);
break;
case SEARCH:
search(&c);
break;
case MODIFY:
modify(&c);
break;
case SORT:
sort(&c);
break;
case SHOW:
show(&c);
break;
case CLEAR:
clear(&c);
break;
case EXIT:
printf("感谢使用,已退出!\n");
break;
default:
printf("输入错误,请输入正确的数字!\n");
}
}
while (input);
return 0;
}
到这里大框架基本就完成了,只要再完成具体功能的实现就可以使用了。
二、具体功能模块的实现(contact.c)
1、辅助函数——通过姓名找到对应联系人数组下标
因为这个函数只在该源文件下使用,所以我把它定义成了静态函数,限定了其作用域,同时增加安全性。至于查找过程没什么难度,也就不再赘述。
static int findByName(contact* c, char* name)
{
int i = 0;
for (i = 0; i < c->num; i++)
{
if (strcmp(c->data[i].name, name) == 0)
return i;
}
return -1;
}
2、显示通讯录
void show(contact* c)
{
printf("%-NAMEs %-SEXs %-AGEs %-PHONEs %-ADDRs\n","姓名","性别","年龄","电话","地址");
int i = 0;
for (i = 0; i < c->num; i++)
{
printf("%-NAMEs %-SEXs %-AGEd %-PHONEs %-ADDRs\n", c->data[i].name, c->data[i].sex, c->data[i].age,
c->data[i].phone, c->data[i].addr);
}
printf("显示完毕!\n");
return;
}
3、增加联系人
void add(contact* c)
{
printf("请输入需要添加的联系人信息(姓名,性别,年龄,电话,地址):\n");
scanf("%s %s %d %s %s", c->data[c->num].name, c->data[c->num].sex, &(c->data[c->num].age),
c->data[c->num].phone, c->data[c->num].addr);
c->num++;
printf("添加成功!\n");
return;
}
这里只说一点,增加完联系人之后不能忘记给联系人数组中的联系人数量num加1。
4、删除联系人
这里就用到了前面的辅助函数,先根据名字找到人,通过覆盖把要删除的联系人覆盖掉,再给联系人数组中的联系人数量num减一。
void del(contact* c)
{
if (c->num == 0)
{
printf("您的通讯录现在还没有联系人,请先添加喔!\n");
}
else
{
printf("请输入需要删除的联系人姓名:");
char name[NAME];
scanf("%s", name);
int ret = findByName(c, name);
printf("\n");
if (ret == -1)
{
printf("对不起,查无此人呢!\n");
}
else
{
int i = 0;
for (i = ret; i < c->num - 1; i++)
{
c->data[i] = c->data[i + 1];
}
c->num--;
printf("删除完毕!\n");
}
}
return;
}
5、查找联系人
这部分其实没有做什么实现,直接调用辅助函数,再加一下提示就好了。
void search(contact* c)
{
if (c->num == 0)
{
printf("您的通讯录现在还没有联系人,请先添加喔!\n");
}
else
{
printf("请输入需要查找的联系人姓名:");
char name[NAME];
scanf("%s", name);
int ret = findByName(c, name);
printf("\n");
if (ret == -1)
{
printf("对不起,查无此人呢!\n");
}
else
{
printf("哇哦!找到了!\n");
printf("\n");
printf("%%-NAMEs %-SEXs %-AGEs %-PHONEs %-ADDRs\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-NAMEs %-SEXs %-AGEd %-PHONEs %-ADDRs\n", c->data[ret].name, c->data[ret].sex, c->data[ret].age,
c->data[ret].phone, c->data[ret].addr);
}
}
return;
}
6、修改联系人
也是借助辅助函数,找到对应联系人之后,把修改后的信息重新赋给它,直接覆盖就好。
void modify(contact* c)
{
if (c->num == 0)
{
printf("您的通讯录现在还没有联系人,请先添加喔!\n");
}
else
{
printf("请输入需要修改的联系人姓名:");
char name[NAME];
scanf("%s", name);
int ret = findByName(c, name);
printf("\n");
if (ret == -1)
{
printf("对不起,查无此人呢!\n");
}
else
{
printf("请输入修改后的联系人信息(姓名,性别,年龄,电话,地址):\n");
scanf("%s %s %d %s %s", c->data[ret].name, c->data[ret].sex,&( c->data[ret].age),
c->data[ret].phone, c->data[ret].addr);
printf("修改成功!\n");
}
}
return;
}
7、清空联系人
这部分直接调用一个memset把结构体数组置 0 ,编译器会自动识别为结束标识符,就可以完成通讯录的清空。
void clear(contact* c)
{
memset(c, 0, sizeof(*c));//c是指针,不是数组
printf("清空完毕!\n");
return;
}
8、对联系人排序
因为排序方式有很多,在这个通讯录中可以通过姓名、性别、年龄排序,所以封装了3个静态函数分别实现这个功能,再通过总的排序函数调用。需要注意的是,要把这三个静态函数放到总排序函数前,要不然就要做好声明,因为这三个静态函数是只作用在该源文件内部,只有放到调用之前才可以不做声明。
排序是采用的升序的冒泡排序。众所周知,冒泡排序是双重for循环去实现的,外面一层表示要排序的趟数,里面一层把每趟的最大值放到最后,若有不符合直接交换位置。这里可以做一个小小的优化,设立一个岗哨 flag ,若是有一趟全部都没有交换位置,表示已经是升序了,这时直接跳出循环,排序完毕!
(1)排序总函数
void sort(contact* c)
{
printf("请选择排序方式对应的数字(1.姓名/2.性别/3.年龄):");
int sortnum = 0;
scanf("%d", &sortnum);
switch (sortnum)
{
case 1:
sortByName(c);
break;
case 2:
sortBySex(c);
break;
case 3:
sortByAge(c);
break;
default:
printf("请输入规定数字:\n");
break;
}
return;
}
(2)通过“姓名”排序
static void sortByName(contact* c)
{
int i = 0;
for (i = 0; i < c->num - 1; i++)
{
int flag = 0;
int j = 0;
for (j = 0; j < c->num - 1 - i; j++)
{
if (strcmp(c->data[j].name, c->data[j + 1].name) > 0)
{
people tmp = c->data[j];
c->data[j] = c->data[j + 1];
c->data[j + 1] = tmp;
flag = 1;
}
}
if (flag == 0)
{
break;
}
}
printf("按名字排序完毕!\n");
return;
}
(3)通过“性别”排序
static void sortBySex(contact* c)
{
int i = 0;
for (i = 0; i < c->num - 1; i++)
{
int flag = 0;
int j = 0;
for (j = 0; j < c->num - 1 - i; j++)
{
if (strcmp(c->data[i].sex, c->data[i + 1].sex) > 0)
{
people tmp = c->data[i];
c->data[i] = c->data[i + 1];
c->data[i + 1] = tmp;
flag = 1;
}
}
if (flag == 0)
{
break;
}
}
printf("按性别排序完毕!\n");
return;
}
(4)通过“年龄”排序
static void sortByAge(contact* c)
{
int i = 0;
for (i = 0; i < c->num - 1; i++)
{
int flag = 0;
int j = 0;
for (j = 0; j < c->num - 1 - i; j++)
{
if (c->data[i].age > c->data[i + 1].age)
{
people tmp = c->data[i];
c->data[i] = c->data[i + 1];
c->data[i + 1] = tmp;
flag = 1;
}
}
if (flag == 0)
{
break;
}
}
printf("按年龄排序完毕!\n");
return;
}
三、总结收获
这已经是写的第三个跨文件代码,感觉已经基本摸清了这类程序的套路。这次的实现相较前两个简单很多,没有很复杂的逻辑,当然功能也很单一薄弱。没写之前觉得很难,写了之后就觉得很多东西其实没有想象的那么难,前提是你要行动!!!哈哈哈,强行鸡汤,反正是我的真实感受,这次分享这就结束了~拜拜