程序介绍
通过前面学习我们可以实现通讯录小程序的代码,首先,我们需要创建一个通讯录,通讯录中存放1000个人的信息,信息包括:名字,性别,年龄,电话,住址。通讯录主要由5个功能:增加联系人、删除联系人、修改联系人、查找联系人、按名称子排序。下面我们来看实现过程。
实现思路和内容概括
首先我们将完整的代码分为3个部分——contact.h(头文件)、contact.c(函数部分)、test.c(主函数实现部分)。我们会创建一个大的框架,将菜单放入其中,菜单有7个选项:1.add 2.del 3.search 4.modify 5.show 6.sort 0.exit 输入“1”,添加联系人信息,输入“2”,删除指定联系人信息,输入“3”,查找指定联系人信息(按姓名查找并打印),输入“4”,修改指定联系人信息(按姓名查找),输入“5”,打印当前的通讯录所有联系人,输入“6”,将通讯录所有联系人按姓名排序。输入“0”,退出通讯录。
代码实现
打印菜单
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");
printf("****************************\n");
}
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
int main()
{
int input=0;
struct 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:
SortContactByName(&con);
break;
case EXIT:
printf("退出通讯录。\n");
break;
default:
printf("选择错误!\n");
break;
}
} while (input);
return 0;
}
我们利用枚举类型将EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT放入其中,因为枚举类型中的成员属于常量所以具有常量的属性,我们可以利用此属性运用到switch上,这样就提高了代码的可读性。利用do while循环,我们先打印menu()函数,再利用input变量,只要输入非0,都会打印菜单,只是效果不同。
定义通讯录
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define MAX 1000
struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char addr[ADDR_MAX];
};
struct Contact
{
struct PeoInfo data[MAX];
int sz ;
};
定义一个通讯录需要两个结构体,struct PeoInfo结构体是描述一个人的信息,struct Contact结构体是通讯录本身,我们将struct PeoInfo中的个人信息存放在struct Contact结构体中以实现联系,也就是struct Contact的成员data[1000]是struct PeoInfo结构体类型,data[1000]是一个结构体类型的数组,里面有name[20],age,sex[5],tele[12],addr[30]。
初始化通讯录函数
void InitContact(struct Contact* pc)
{
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
当我们定义一个通讯录结构体时,数值都是随机值,因此我们需要初始化,将通讯录所有的信息数据都变成“0”,这里使用了memset()函数,我们在内存函数博客中介绍了这个函数,不在细说。具体实现原理可以看前面的博客内存函数(C语言)
添加联系人函数
void AddContact(struct Contact* pc)
{
if (pc->sz == MAX)
{
printf("通讯录已满!\n");
}
else
{
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 );
printf("添加成功\n");
pc->sz++;
}
}
AddContact()函数,首先先进行判断,sz(记录当前存放进通讯录中的有效信息的个数)是否等于MAX(1000),如果有,则打印通讯录已满,如果没有则按要求输入信息,并提示添加成功,sz++。
显示通讯录函数
void ShowContact(struct Contact* pc)
{
int i = 0;
printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->sz; i++)
{
printf("%15s\t%5d\t%8s\t%15s\t%30s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
在ShowContact()函数中,我们先打印标题"name", “age”, “sex”, “tele”, “addr”,然后利用for循环将信息一一对应标题都打印下来。
查找通讯录函数
int FindContactByName(const struct Contact* pc, const char *name)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
FindContactByName()函数的基本实现是通过strcmp()函数实现的,利用for循环,找到查找的名字于通讯录名字相等的此时i的值,并返回i的值,此时i的值正是data数组的下标,方便后续操作,关于strcmp()函数实现原理可以看以前博客字符与字符串函数(C语言)。
删除联系人
void DelContact(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
char name[NAME_MAX] = { 0 };
printf("请输入要删除人的名字:>");
scanf("%s", name);
int pos = FindContactByName(pc, name);
if (pos == -1)
{
printf("指定的联系人不存在\n");
}
else
{
int j = 0;
for (j = pos; j < pc->sz-1; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
DelContact()函数和AddContact()函数类似,但是比添加联系人要复杂,删除联系人先再找到指定的联系人,在进行操作,因此,DelContact()函数中运用了查找通讯录函数,利用名字进行比较找出指定的联系人,在进行删除时,并不是直接删除,而是利用后一个信息将前一个信息的覆盖,如此达到删除联系人的信息。
查找指定联系人
void SearchContact(const struct Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("输入要查找人的名字:>");
scanf("%s", name);
int pos = FindContactByName(pc, name);
if (-1 == pos)
{
printf("查无此人\n");
}
else
{
printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
printf("%15s\t%5d\t%8s\t%15s\t%30s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
SearchContact()函数是在FindContactByName()函数的基础之上增加了打印的功能,如果没有查找到指定的联系人打印“查无此人”,否则打印出该联系人的所有信息。
修改指定联系人信息
void ModifyContact(struct Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("输入要修改人的名字:>");
scanf("%s", name);
int pos = FindContactByName(pc, name);
if (-1 == pos)
{
printf("要修改的人不存在\n");
}
else
{
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].tele);
printf("请输入新的地址:>");
scanf("%s", pc->data[pos].addr);
}
}
ModifyContact()函数是利用FindContactByName()函数先将想要修改的联系人找到,然后输入新的所有信息来覆盖原来的信息。
排序(按姓名)
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct Contact*)e1)->data->name , ((struct Contact*)e2)->data->name);
}
void SortContactByName(struct Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
printf("排序成功\n");
}
排序运用到了qsort()函数,这个函数可以排序任意类型的数据,因此结构体也可以。但是需要引用到头文件#include<stdlib.h> qsort()函数: 形式:void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) ) 解释:qsort函数实现了一种快速排序算法,用于对num元素数组进行排序,每个元素的宽度为字节。参数base是指向要排序的数组的基的指针。qsort用已排序的元素覆盖此数组。参数compare是指向用户提供的例程的指针,该例程比较两个数组元素并返回指定其关系的值。 也就是说,*base是一个指针,num是要排序的个数,width是排序元素的大小,int (__cdecl *compare )(const void *elem1, const void *elem2 ) 是一个函数,这个函数就是用来判断怎样比较的。以写的代码为例,pc是指针指向的是struct Contact con结构体,pc->sz是记录当前存放进通讯录中的有效信息的个数,也就对应了打印的个数,sizeof(pc->data[0])是个人信息的结构体的大小,cmp_name()函数是按名字来进行排序。
完整代码
contact.h
#include<string.h>
#include <stdio.h>
#include<stdlib.h>
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 12
#define ADDR_MAX 30
#define MAX 1000
struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char addr[ADDR_MAX];
};
struct Contact
{
struct PeoInfo data[MAX];
int sz ;
};
void InitContact(struct Contact* pc);
void AddContact(struct Contact* pc);
void ShowContact(struct Contact* pc);
void DelContact(struct Contact* pc);
void SearchContact(const struct Contact* pc);
void ModifyContact(struct Contact* pc);
void SortContactByName(struct Contact* pc);
用#define定义的常量能够方便代码的修改,增加了代码的维护性。
contact.c
#include"contact.h"
void InitContact(struct Contact* pc)
{
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(struct Contact* pc)
{
if (pc->sz == MAX)
{
printf("通讯录已满!\n");
}
else
{
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 );
printf("添加成功\n");
pc->sz++;
}
}
void ShowContact(struct Contact* pc)
{
int i = 0;
printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->sz; i++)
{
printf("%15s\t%5d\t%8s\t%15s\t%30s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
int FindContactByName(const struct Contact* pc, const char *name)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)
{
return i;
}
}
return -1;
}
void DelContact(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("通讯录为空,无法删除\n");
return;
}
char name[NAME_MAX] = { 0 };
printf("请输入要删除人的名字:>");
scanf("%s", name);
int pos = FindContactByName(pc, name);
if (pos == -1)
{
printf("指定的联系人不存在\n");
}
else
{
int j = 0;
for (j = pos; j < pc->sz-1; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("删除成功\n");
}
}
void SearchContact(const struct Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("输入要查找人的名字:>");
scanf("%s", name);
int pos = FindContactByName(pc, name);
if (-1 == pos)
{
printf("查无此人\n");
}
else
{
printf("%15s\t%5s\t%8s\t%15s\t%30s\n\n", "name", "age", "sex", "tele", "addr");
printf("%15s\t%5d\t%8s\t%15s\t%30s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
void ModifyContact(struct Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("输入要修改人的名字:>");
scanf("%s", name);
int pos = FindContactByName(pc, name);
if (-1 == pos)
{
printf("要修改的人不存在\n");
}
else
{
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].tele);
printf("请输入新的地址:>");
scanf("%s", pc->data[pos].addr);
}
}
int cmp_name(const void* e1, const void* e2)
{
return strcmp(((struct Contact*)e1)->data->name , ((struct Contact*)e2)->data->name);
}
void SortContactByName(struct Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(pc->data[0]), cmp_name);
printf("排序成功\n");
}
存放用到的所有函数,需要引用头文件#include"contact.h"。
test.c
#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");
printf("****************************\n");
}
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
int main()
{
int input=0;
struct 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:
SortContactByName(&con);
break;
case EXIT:
printf("退出通讯录。\n");
break;
default:
printf("选择错误!\n");
break;
}
} while (input);
return 0;
}
主函数部分,这个部分也是实现部分,进入函数,先创建一个通讯录struct Contact con,然后初始化,再根据所选的input,进行个系列的操作,以此达到通讯录的增删改查等功能。
运行展示
总结
关于通讯录其实还有很多改进的地方,这只是最简易的版本,还可以加入文件导入,动态版本等等,相对于改进的版本,这是最简易也是最好理解的。有难度的在于qsort()函数的理解,当然我们也可以采用自己实现的排序冒泡或者快排都可以。好了通讯录小程序也可以说的上将C语言中语法的大部分内容都用了一边,对于编程能力的提升和语法的理解是有着很大的帮助的,希望大家能够抽出时间将代码敲上一敲。如果有什么错误或者建议,希望各位能够提出建议,谢谢各位!
|