1.实现功能
本文将采用C语言来实现一个简单的通讯录,要求功能如下
代码语言:javascript
复制
//实现一个通讯录 | |
//1.可以保存100个人的信息 | |
//2.增加人的信息 | |
//3.删除指定联系人的信息 | |
//4.查找 | |
//5.修改 | |
//6.排序 | |
//7.显示所有联系人 |
下面我们来一步步实现
2.实现细节与具体思路
1.主程序设计
首先,设计一个主程序来对于通讯录进行一个整体的设计,它的功能我们用函数来进行包装,这样可以增加代码的可读性。
代码语言:javascript
复制
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
复制
//类型声明 | |
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
复制
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"); | |
} |