前言
前段时间完善了自己之前写的通讯录程序。程序用c语言进行编写,分为使用静态版本的v1版程序,和使用动态内存管理的v2版程序。
通讯录中存储的个人信息包括:姓名、性别、年龄、电话、住址。
要求通讯录包含以下功能:
//1 添加联系人信息
//2 删除指定联系人信息
//3 查找指定联系人信息
//4 修改指定联系人信息
//5 显示所有联系人信息
//6 清空所有联系人
//7 以名字排序所有联系人
v1-静态版本的通讯录程序:可以存储1000个联系人信息的通讯录功能实现
编写思路
编程使用vs2022完成。采用了分文件编写的思路,将程序分为自定义头文件contact.h
,源文件contact.c
和test.c
。思路如下:
contact.h
包含程序所需的头文件和宏定义、对类型的声明和对函数的声明。contact.c
包含对函数功能的具体实现,囊括了通讯录共7个功能的函数实现。test.c
是测试用源文件,包含了通讯录程序的总体框架与main函数。
以下对v1版本通讯录程序进行具体介绍。
实现通讯录功能的前期准备
1. 我需要一个菜单
在拿到实现通讯录功能的这个题目后,我首先想到的是程序打开后呈现给使用者的交互界面。程序有义务为使用者说明自身功能并提供提示。因此在编写的开始,我需要一个菜单为使用者提供指引。
void menu()
{
printf("******************************************\n");
printf("**** meun ****\n");
printf("**** 1.add 2.del ****\n");
printf("**** 3.search 4.modify ****\n");
printf("**** 5.print 6.clear ****\n");
printf("**** 7.sort_by_name 0.exit ****\n");
printf("******************************************\n");
}
程序共7个功能,在菜单上一一罗列出来。并加入了退出选项。
程序运行效果如下:
为方便后续代码编写,menu()
写在测试源文件test.c
中。无需在头文件contact.h
中进行声明。
2. 我需要多个接口
接下来是程序总体框架的搭建。在面对多个功能的选择时,选用 switch case
语句是很容易想到的。
我需要创建一个输入值num
,当输入不同的num
值时,进入相对应的接口完成函数调用,从而实现相应的通讯录功能。
考虑到通讯录在使用时,存在需要多次输入、反复调用不同功能的情况,因此需要将switch case
放进循环中。
为了调用菜单函数menu()
,并完成对num
的读取操作,使用do while
循环以提供多次输入功能。
代码如下:
int num = 0;//输入值
do
{
menu();
printf("请输入->");
scanf("%d", &num);
switch (num)
{
case 1:
add_contact(&data);
break;
case 2:
del_contact(&data);
break;
case 3:
search_contact(&data);
break;
case 4:
modify_contact(&data);
break;
case 5:
print_contact(&data);
break;
case 6:
init_contact(&data);
break;
case 7:
sort_by_name_contact(&data);
break;
case 0:
printf("退出通讯录。\n");
break;
default://switch语句的默认选项
printf("请输入正确的num值。\n");
break;
}
} while (num);
2.1 使用枚举类型增加代码可读性
值得注意的是,在case
语句中,使用1、2、3
的表示形式,容易在编写时造成可读性低的麻烦,从而导致不必要的编写错误。我选用声明一个枚举类型的方式,用ADD、DEL
等字符串代替数字,增加程序的可读性。
枚举类型应写在头文件contact.h
中,声明如下:
enum option
{
//使用枚举,增加可读性
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
PRINT,
CLEAR,
SORT_BY_NAME
};
由枚举类型的语法定义知:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
故EXIT == 0,ADD == 1
,以此类推。
枚举类型的功能与宏定义功能一致,但代码更加简洁。
若用宏定义实现,代码如下:
#define EXIT 0
#define ADD 1
#define DEL 2
...
相比之下明显选择使用枚举类型更加简便。
至此,test.c
文件中的程序总体框架基本搭建完毕。
3. 我需要声明并创建结构体
题目要求通讯录能够存储姓名、性别、年龄、电话、住址等信息,所以可创建结构体struct peoinfo
,对以上信息进行存储。并使用typedef
对结构体名称进行简化。
此结构体声明应当放在头文件contact.h
中。代码如下:
typedef struct peoinfo
{
char name[20];
char gender[10];
int age;
char tele[20];
char address[20];
}peoinfo;
结构体类型peoinfo
可以存储通讯录所需的各种信息。但值得注意的是,通讯录中应存在许多peoinfo
结构体,用来存储不同人的信息。因此我还需要一个整形参数sz
对通讯录中已存储的信息进行计数。
考虑到通讯录有7个或更多功能,需要编写多个函数。每次传入peoinfo
数组和整形参数sz
太过繁琐。因此将proinfo
数组和整形参数sz
封装进一个新构造体struct contact
。
声明如下:
typedef struct contact
{
//创建通讯录
peoinfo contact[Con_MAX];
int sz;//目前已存储联系人数
//注意在定义结构体时不能在结构体中进行初始化!!!
}contact;
peoinfo contact[Con_MAX];
为了方便修改通讯录可储存的信息个数,在头文件contact.h
中使用宏定义,#define Con_MAX 1000
。
在后续的函数传参中,只需要传入contact
类型变量的地址,就可以完成对通讯录中信息的修改了。
在进入do while
循环前,创建结构体变量contact data;
。
3.1 不能在结构体声明中对结构体参数进行初始化
这里在一开始进行封装时我犯了一个错误,将sz
在结构体声明中初始化了。在声明中只能出现int sz;
,而不能出现int sz = 0;
。
4. 对结构体data进行初始化
由于结构体contact
中嵌套有结构体peoinfo
,为方便后续可能存在的修改,并提高程序安全性,我需要单独写一个函数将contact data
进行初始化。
函数在头文件contact.h
中进行声明,在源文件contact.c
中进行实现,在源文件test.c
中进行调用。
函数实现如下:
void init_contact(contact* pc)
{
pc->sz = 0;
memset(pc->contact, 0, Con_MAX * sizeof(peoinfo));//初始化为0
}
由于结构体contact data
过大,且需要修改data
的值,函数使用指针传参。初始化整形变量sz
为0,使用库函数memset
对结构体所占内存的每一个字节进行置0操作。在这里记得要在contact.h
中引用头文件<string.h>
。
初始化函数在创建结构体contact data
后调用。
5. 对test()函数进行初步封装
现在我已经完成了除实现通讯录功能所需的函数之外的一切工作。在此,对test.c
中的测试函数test()
进行初步封装。其中所有函数调用均为预留,在这一步程序可以运行,但无法实现通讯录要求的任何功能。程序运行前需要将函数调用注释掉。
测试函数test()
代码如下:
void test()
{
//创建并初始化输入值
int num = 0;
//创建通讯录
contact data;
//初始化通讯录
init_contact(&data);
do
{
menu();
printf("请输入->");
scanf("%d", &num);
switch (num)
{
case ADD:
add_contact(&data);
break;
case DEL:
del_contact(&data);
break;
case SEARCH:
search_contact(&data);
break;
case MODIFY:
modify_contact(&data);
break;
case PRINT:
print_contact(&data);
break;
case CLEAR:
init_contact(&data);
printf("已清空通讯录。\n");
break;
case SORT_BY_NAME:
sort_by_name_contact(&data);
break;
case 0:
printf("退出通讯录。\n");
break;
default://switch语句的默认选项
printf("请输入正确的num值。\n");
break;
}
} while (num);
}
通讯录各功能的具体实现
本通讯录要求实现共7个功能,为每个功能编写相应的函数。
在头文件contact.h
中进行函数声明,在源文件contact.c
中进行函数实现,在test.c
中进行函数调用。以下不再赘述。
1. 添加联系人
void add_contact(contact* pc)
{
if (pc->sz == Con_MAX)
{
printf("通讯录已满。\n");
return;
}
printf("添加第%d位联系人:>\n", pc->sz + 1);
printf("请输入姓名:>");
scanf("%s", pc->contact[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->contact[pc->sz].gender);
printf("请输入年龄:>");
scanf("%d", &(pc->contact[pc->sz].age));
printf("请输入电话:>");
scanf("%s", pc->contact[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->contact[pc->sz].address);
pc->sz++;
printf("添加成功。\n");
}
首先判断通讯录是否已满,如通讯录已满,则输出提示**通讯录已满。**并退出添加联系人函数。
添加联系人的功能实现很简单,不过在编写时需要注意读入不同类型参数时,scanf()
所需的类型说明符%d,%s
。避免在细节上犯错。
2. 删除联系人
void del_contact(contact* pc)
{
//根据姓名
char name_tmp[20];
printf("请输入想删除联系人的姓名:>");
scanf("%s", name_tmp);
//比对
for (int i = 0; i < pc->sz; i++) {
if (strcmp(name_tmp, pc->contact[i].name) == 0)
{
//将i+1位置挪至i位置
for (int j = i; j < pc->sz - 1; j++)//注意是sz-1,如果j<sz,则会发生越界
{
pc->contact[j] = pc->contact[j + 1];
memset(pc->contact + j + 1, 0, sizeof(peoinfo));
}
pc->sz--;
printf("删除成功。\n");
return;
}
}
}
我采用根据姓名删除相应的联系人。使用strcmp
函数将输入的姓名与通讯录中所存储的姓名一一进行比对,此处不考虑重名的出现。
删除联系人的底层逻辑是,将内存中后面存储的联系人信息统一向前移动一位,即使pc->contact[j + 1]
处的数据存入pc->contact[j]
中。并将pc->contact[j + 1]
中的数据清空。
完成覆盖后,再记得将sz--
。
3. 查找联系人
void search_contact(contact* pc)
{
//根据姓名
char name_tmp[20];
printf("请输入想查找联系人的姓名:>");
scanf("%s", name_tmp);
//比对
for (int i = 0; i < pc->sz; i++) {
if (strcmp(name_tmp, pc->contact[i].name) == 0)
{
printf("找到此人。\n");
printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
return;
}
}
printf("此人未在通讯录中。\n");
}
我选择通过姓名查找的方式。数据的输入和比对与删除联系人函数类似。在找到想要查找的人后,对此人的全部信息进行打印。
printf(“%-20s %-5s %-5s %-16s %-30s\n”, “姓名”, “年龄”, “性别”, “电话”, “住址”);
printf(“%-20s %-5d %-5s %-16s %-30s\n”, pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
这里的%-20s
是为了使显示出的数据对齐。%-20s
表示左对齐20个字符,%20s
表示右对齐20个字符。
printf(“%-20s %-5s %-5s %-16s %-30s\n”, “姓名”, “年龄”, “性别”, “电话”, “住址”);
在希望输出常量时,这样的写法给我启发。
4. 修改联系人信息
void modify_contact(contact* pc)
{
//根据姓名
char name_tmp[20];
printf("请输入想修改联系人的姓名:>");
scanf("%s", name_tmp);
//比对
for (int i = 0; i < pc->sz; i++) {
if (strcmp(name_tmp, pc->contact[i].name) == 0)
{
int input_tmp = 0;
do {
printf("需要修改此人的哪一项信息?\n");
printf("1.姓名 2.年龄 3.性别 4.电话 5.住址 0.不修改\n");
scanf("%d", &input_tmp);
char name[20];
char gender[10];
int age;
char tele[20];
char address[20];
switch (input_tmp)
{
case 1:
printf("请输入修改后的姓名:>");
scanf("%s", name);
//将原内容清空
memset(pc->contact[i].name, 0, strlen(pc->contact[i].name));
strncpy(pc->contact[i].name, name,strlen(name));
printf("修改成功。\n");
break;
case 2:
printf("请输入修改后的年龄:>");
scanf("%d", &age);
pc->contact[i].age = age;
printf("修改成功。\n");
break;
case 3:
printf("请输入修改后的性别:>");
scanf("%s", gender);
memset(pc->contact[i].gender, 0, strlen(pc->contact[i].gender));
strncpy(pc->contact[i].gender, gender, strlen(gender));
printf("修改成功。\n");
break;
case 4:
printf("请输入修改后的电话:>");
scanf("%s", tele);
memset(pc->contact[i].tele, 0, strlen(pc->contact[i].tele));
strncpy(pc->contact[i].tele, tele,strlen(tele));
printf("修改成功。\n");
break;
case 5:
printf("请输入修改后的地址:>");
scanf("%s", address);
memset(pc->contact[i].address, 0, strlen(pc->contact[i].address));
strncpy(pc->contact[i].address, address,strlen(address));
printf("修改成功。\n");
break;
case 0:
printf("退出修改。\n");
break;
default:
printf("请输入正确的值。\n");
break;
}
} while (input_tmp);
return;
}
}
printf("查无此人。\n");
}
修改联系人信息的功能相对而言代码量大一些。采用了与整体框架相类似的do while
语句和switch case
语句的嵌套。
需要注意的也是细节之处。比如在将临时变量name[20]
中存储的字符串拷贝至通讯录中存储的字符串空间pc->contact[i].name
时,需要调用库函数strcpy
,在第一次编写时,我犯了pc->contact[i].name =name
的错误,实属不该。
注意在修改前,应当将已有内容先进行清空,再调用strcpy
函数。
5. 显示联系人信息
//显示通讯录
void print_contact(contact* pc)
{
assert(pc);
printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
//当通讯录为空时
if (pc->sz == 0)
{
printf("通讯录为空,请添加联系人后打印。\n");
return;
}
for (int i = 0; i < pc->sz; i++)
{
printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
}
printf("打印完毕。\n");
}
其底层逻辑在上述查找联系人函数中已有说明,此处不再赘述。
6. 清空通讯录
此处直接调用初始化函数即可。
7. 根据姓名进行排序 - 调用库函数qsort
int compar_by_name(const void* pc1, const void* pc2)
{
return strcmp(((peoinfo*)pc1)->name, ((peoinfo*)pc2)->name);
}
void sort_by_name_contact(contact* pc)
{
qsort(pc->contact, pc->sz, sizeof(peoinfo), compar_by_name);
printf("排序完毕。\n");
}
7.1 对qsort()函数的简介
此处调用了c语言库函数qsort,需要引用头文件<stdlib.h>
。其函数声明为:
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void , const void))
各参数的解释如下:
base
– 指向要排序的数组的第一个元素的指针。nitems
– 由 base 指向的数组中元素的个数。size
– 数组中每个元素的大小,以字节为单位。compar
– 用来比较两个元素的函数。
其中compar函数是需要我们自己写的,在这里因为是根据姓名进行排序,所以我编写了比较函数int compar_by_name(const void* pc1, const void* pc2)
。
通过查阅MSDN,得知compar函数的判断逻辑如下:
compar函数的返回值规定为int类型,默认以升序进行排列。当返回值小于0时,表示第一个元素小于第二个元素,排序为元素1,元素2
;当返回值为0时,不改变两元素排序;当返回值大于0时,排序为元素2,元素1
。
各功能测试视频
通讯录功能测试
通过视频可以看出,此程序各功能正常,均已实现。
V2-加入动态内存管理后的通讯录程序
编写思路
在学习了c语言中动态内存管理部分的知识后,引入头文件<stdlib.h>
,使用库函数malloc、realloc、free等对V1版本的通讯录程序进行优化。
默认程序运行起来时,能存放3个人的信息(可以是任何值,设置为3方便测试),如通讯录存储已满,则再向内存申请两个peoinfo类型的空间。实现动态内存管理。
程序优化
1. struct contact 通讯录结构体改进
typedef struct contact
{
//创建通讯录
peoinfo* contact;
int sz;//目前已存储联系人数
int capacity;//记录通讯录当前可存储联系人数
}contact;
peoinfo* contact;
如需使用malloc函数进行动态内存开辟,则contact结构体中无需创建数组,而应该修改为创建一个peoinfo类型的指针,指向一段空间。在程序中使用malloc函数在堆区开辟空间。
创建整型变量capacity
,是为了进行下一步的增容操作。每次完成增加联系人操作时,判断sz和capacity的大小关系。当sz == capacity
时,进行增容。
2. init_contact(contact* pc)初始化结构体函数改进
修改果结构体struct contact
后,相应的初始化函数也需要进行修改。这里即体现出将初始化结构体这一行为封装成函数的智慧,方便后续的修改和优化。
修改后函数如下:
// 初始化通讯录
void init_contact(contact* pc)
{
pc->sz = 0;
pc->capacity = 3;
pc->contact = (peoinfo*)malloc(pc->capacity * sizeof(peoinfo));
//开辟后进行判空操作
if(pc->contact == NULL)
{
perror("init_contact::malloc");//表明在init_contact函数内部调用malloc时出现问题
return;
}
memset(pc->contact, 0, pc->capacity * sizeof(peoinfo));//初始化为0
在此实现通讯录运行时,默认能够存储三个人信息。这里capacity的初始化数值也可用define定义为一个符号。
3. 动态内存管理的实现 - realloc
当通讯录中已存入3个人的信息时,使用realloc函数对通讯录进行扩容。应对增加联系人函数进行修改:
void add_contact(contact* pc)
{
if (pc->sz == pc->capacity)
{
//printf("通讯录已满。\n");
//return;
peoinfo* tmp=(peoinfo*)realloc(pc->contact, (pc->capacity + 2) * sizeof(peoinfo));
if(tmp != NULL)
{
pc->contact = tmp;
}
pc->capacity += 2;
}
//以下部分和静态版本一样
printf("添加第%d位联系人:>\n", pc->sz + 1);
printf("请输入姓名:>");
scanf("%s", pc->contact[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->contact[pc->sz].gender);
printf("请输入年龄:>");
scanf("%d", &(pc->contact[pc->sz].age));
printf("请输入电话:>");
scanf("%s", pc->contact[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->contact[pc->sz].address);
pc->sz++;
printf("添加成功。\n");
}
其中新添加的增容代码如下,可将其封装成一个函数:
void check_if_full(contact* pc)
{
if (pc->sz == pc->capacity)
{
//printf("通讯录已满。\n");
//return;
peoinfo* tmp=(peoinfo*)realloc(pc->contact, (pc->capacity + 2) * sizeof(peoinfo));
if(tmp != NULL)
{
pc->contact = tmp;
}
pc->capacity += 2;
printf("增容成功");
}
}
当调用增加联系人函数时,对通讯录可存储的联系人数有影响。此时加入此先判断再增容功能,完善程序的动态内存管理机制。每当通讯录存满,就又会新开辟两个peoinfo类型的内存空间。
4. 内存空间释放 - free
当退出通讯录时,对动态开辟的内存空间进行释放。
封装一个函数destroy_contact()
,在exit时调用。
void destroy_contact(contact* pc)
{
free(pc->contact);
pc->contact = NULL;
pc->sz = 0;
pc->capacity = 0;
}
总结
总的来说,通讯录程序的实现,逻辑并不复杂。总代码量约在330行左右,锻炼了分文件编程思维,对string库函数的使用,动态内存管理函数malloc、free、realloc的使用,memset等内存管理函数的使用、qsort快排库函数的使用等。完善程序至今,我的代码编写、调试能力也得到很大提升。
源码
附V1版本源码如下,V2版本请参考文中内容。
供各位参考:
contact.h:
#pragma once
//头文件声明
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
//#define _CRT_SECURE_NO_WARNINGS 1
#define Con_MAX 1000
//类型声明
//个人信息结构体
typedef struct peoinfo
{
char name[20];
char gender[10];
int age;
char tele[20];
char address[20];
}peoinfo;
//通讯录结构体,将数组和计数封装进一个结构体中
typedef struct contact
{
//创建通讯录
peoinfo contact[Con_MAX];
int sz;//目前已存储联系人数
//注意在定义结构体时不能在结构体中进行初始化!!!
}contact;
//枚举类型
enum option
{
//使用枚举,增加可读性
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
PRINT,
CLEAR,
SORT_BY_NAME
};
//函数声明
//打印菜单
//void menu();
//初始化通讯录
void init_contact(contact* pc);
//添加联系人
void add_contact(contact* pc);
void del_contact(contact* pc);
void search_contact(contact* pc);
void modify_contact(contact* pc);
void print_contact(contact* pc);
int compar_by_name(const void* name1, const void* name2);//快排比较函数
void sort_by_name_contact(contact* pc);
contact.c:
#include"contact.h"
//函数实现
//
// 初始化通讯录
void init_contact(contact* pc)
{
pc->sz = 0;
memset(pc->contact, 0, Con_MAX * sizeof(peoinfo));//初始化为0
}
//添加联系人
void add_contact(contact* pc)
{
if (pc->sz == Con_MAX)
{
printf("通讯录已满。\n");
return;
}
printf("添加第%d位联系人:>\n", pc->sz + 1);
printf("请输入姓名:>");
scanf("%s", pc->contact[pc->sz].name);
printf("请输入性别:>");
scanf("%s", pc->contact[pc->sz].gender);
printf("请输入年龄:>");
scanf("%d", &(pc->contact[pc->sz].age));
printf("请输入电话:>");
scanf("%s", pc->contact[pc->sz].tele);
printf("请输入地址:>");
scanf("%s", pc->contact[pc->sz].address);
pc->sz++;
printf("添加成功。\n");
}
//删除联系人
void del_contact(contact* pc)
{
//根据姓名
char name_tmp[20];
printf("请输入想删除联系人的姓名:>");
scanf("%s", name_tmp);
//比对
for (int i = 0; i < pc->sz; i++) {
if (strcmp(name_tmp, pc->contact[i].name) == 0)
{
//将i+1位置挪至i位置
for (int j = i; j < pc->sz - 1; j++)//注意是sz-1,如果j<sz,则会发生越界
{
pc->contact[j] = pc->contact[j + 1];
memset(pc->contact + j + 1, 0, sizeof(peoinfo));
}
pc->sz--;
printf("删除成功。\n");
return;
}
}
}
//显示通讯录
void print_contact(contact* pc)
{
assert(pc);
printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
//当通讯录为空时
if (pc->sz == 0)
{
printf("通讯录为空,请添加联系人后打印。\n");
return;
}
for (int i = 0; i < pc->sz; i++)
{
printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
}
printf("打印完毕。\n");
}
//查找联系人
void search_contact(contact* pc)
{
//根据姓名
char name_tmp[20];
printf("请输入想查找联系人的姓名:>");
scanf("%s", name_tmp);
//比对
for (int i = 0; i < pc->sz; i++) {
if (strcmp(name_tmp, pc->contact[i].name) == 0)
{
printf("找到此人。\n");
printf("%-20s %-5s %-5s %-16s %-30s\n", "姓名", "年龄", "性别", "电话", "住址");
printf("%-20s %-5d %-5s %-16s %-30s\n", pc->contact[i].name, pc->contact[i].age, pc->contact[i].gender, pc->contact[i].tele, pc->contact[i].address);
return;
}
}
printf("此人未在通讯录中。\n");
}
//修改通讯录
void modify_contact(contact* pc)
{
//根据姓名
char name_tmp[20];
printf("请输入想修改联系人的姓名:>");
scanf("%s", name_tmp);
//比对
for (int i = 0; i < pc->sz; i++) {
if (strcmp(name_tmp, pc->contact[i].name) == 0)
{
int input_tmp = 0;
do {
printf("需要修改此人的哪一项信息?\n");
printf("1.姓名 2.年龄 3.性别 4.电话 5.住址 0.不修改\n");
scanf("%d", &input_tmp);
char name[20];
char gender[10];
int age;
char tele[20];
char address[20];
switch (input_tmp)
{
case 1:
printf("请输入修改后的姓名:>");
scanf("%s", name);
//将原内容清空
memset(pc->contact[i].name, 0, strlen(pc->contact[i].name));
strncpy(pc->contact[i].name, name,strlen(name));
printf("修改成功。\n");
break;
case 2:
printf("请输入修改后的年龄:>");
scanf("%d", &age);
pc->contact[i].age = age;
printf("修改成功。\n");
break;
case 3:
printf("请输入修改后的性别:>");
scanf("%s", gender);
memset(pc->contact[i].gender, 0, strlen(pc->contact[i].gender));
strncpy(pc->contact[i].gender, gender, strlen(gender));
printf("修改成功。\n");
break;
case 4:
printf("请输入修改后的电话:>");
scanf("%s", tele);
memset(pc->contact[i].tele, 0, strlen(pc->contact[i].tele));
strncpy(pc->contact[i].tele, tele,strlen(tele));
printf("修改成功。\n");
break;
case 5:
printf("请输入修改后的地址:>");
scanf("%s", address);
memset(pc->contact[i].address, 0, strlen(pc->contact[i].address));
strncpy(pc->contact[i].address, address,strlen(address));
printf("修改成功。\n");
break;
case 0:
printf("退出修改。\n");
break;
default:
printf("请输入正确的值。\n");
break;
}
} while (input_tmp);
return;
}
}
printf("查无此人。\n");
}
//按照姓名排序
int compar_by_name(const void* pc1, const void* pc2)
{
return strcmp(((peoinfo*)pc1)->name, ((peoinfo*)pc2)->name);
}
void sort_by_name_contact(contact* pc)
{
qsort(pc->contact, pc->sz, sizeof(peoinfo), compar_by_name);
printf("排序完毕。\n");
}
test.c:
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
//实现一个通讯录;
//
//通讯录可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址
//
//提供方法:
//
//1 添加联系人信息
//2 删除指定联系人信息
//3 查找指定联系人信息
//4 修改指定联系人信息
//5 显示所有联系人信息
//6 清空所有联系人
//7 以名字排序所有联系人
//菜单
void menu()
{
printf("******************************************\n");
printf("**** meun ****\n");
printf("**** 1.add 2.del ****\n");
printf("**** 3.search 4.modify ****\n");
printf("**** 5.print 6.clear ****\n");
printf("**** 7.sort_by_name 0.exit ****\n");
printf("******************************************\n");
}
void test()
{
//测试函数
int num = 0;
//创建通讯录
contact data;
//初始化通讯录
init_contact(&data);
do
{
menu();
printf("请输入->");
scanf("%d", &num);
switch (num)
{
case ADD:
add_contact(&data);
break;
case DEL:
del_contact(&data);
break;
case SEARCH:
search_contact(&data);
break;
case MODIFY:
modify_contact(&data);
break;
case PRINT:
print_contact(&data);
break;
case CLEAR:
init_contact(&data);
printf("已清空通讯录。\n");
break;
case SORT_BY_NAME:
sort_by_name_contact(&data);
break;
case 0:
printf("退出通讯录。\n");
break;
default://switch语句的默认选项
printf("请输入正确的num值。\n");
break;
}
} while (num);
}
int main()
{
test();
return 0;
}