目录
一、通讯录功能需求分析:
二、文件架构分析:
三、代码,分模块呈现:
3.1 主函数(main函数)部分:
3.3?枚举类型的声明部分:
3.4?初始化通讯录部分:
3.5?增加通讯录联系人信息部分:
3.6?显示联系人信息的部分:
3.7 删除联系人信息的部分:
3.8?查找联系人信息的部分:
3.9?修改联系人信息的部分:
3.10?排序联系人信息的部分:
3.11?清空所有联系人信息的部分:
四、代码以文件进行呈现:
4.1?test.c源文件
????????4.2?contact.c源文件
????????4.3?contact.h头文件
一、通讯录功能需求分析:
为了实现通讯录管理系统,为此,要保证实现以下的功能:
能够存放1000个联系人的信息、每个人的信息包含:名字、年龄、性别、电话、地址、除此之外,还是实现:增加人的信息、删除人的信息、修改指定人的信
息、查找指定人的信息、清空联系人的信息、显示联系人的信息、排序通讯录的信息、
二、文件架构分析:
1、test.c? —? 用来测试通讯录的模块、主函数接口引入。
2、contact.h — 关于通讯录相关的 类型的定义、函数的声明、库函数的头文件等、
3、contact.c — 函数的实现、
写完contact.h、contact.c 之后,将两者引用到?test.c?中使用即可满足要求。
三、代码,分模块呈现:
3.1 主函数(main函数)部分:
int main()
{
int input = 0;
//创建通讯录,结构体类型在头文件中定义的,所以要引头文件,MAX的定义在头文件或者该源文件内定义都是可以的,因为包含了头文件
//但是如果把数组和数组元素个数sz定义成一个结构体,就要去头文件中定义,那边就需要MAX,如果MAX在test.c中定义的话,contact.h中就会报错
//既然这样,就直接把MAX定义在contact.h中去,即可。
//PeoInfo data[MAX] = { 0 };//不完全初始化
通讯录中当前有几个元素:
//int sz = 0;
//创建通讯录
Contact con; //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 PRINT:
//虽然只是打印信息,不会改变实参的信息,但是考虑的效率的话,还是使用传址调用比较好,结构体传参最好传地址。
PrintContact(&con);
break;
//清空所有联系人的信息
case CLEAR:
ClearContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新进行选择:>");
break;
}
} while (input);
//如果有同名的,一律操作第一个出现该名字的那个成员,因为遍历是从前往后遍历的,在这里不考虑同名的情况。
return 0;
}
void menu()
{
printf("*********************************\n");
printf("****** 1、add 2、del ******\n");
printf("****** 3、search 4、modify ****\n");
printf("****** 5、sort 6、print******\n");
printf("****** 7、clear 0、exit *****\n");
printf("*********************************\n");
}
在这里注意一下各种功能的编号与switch中的case的选择对应起来。
3.3?枚举类型的声明部分:
enum Option//枚举成员变量从0开始,依次递增1;
{
//枚举中的变量一般都采用大写。
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
PRINT,
CLEAR
};
枚举类型的定义,要注意的是,枚举成员变量按照顺序默认从0开始,依次递增1,也可给枚举成员变量设置初值、
3.4?初始化通讯录部分:
//初始化通讯录函数
void InitContact(Contact* pc)
{
(pc->sz) = 0;
//内存设置函数 — memset() - 内存设置
memset(pc->data, 0, sizeof(pc->data));
//pc->data 就相当于找到了整个数组,而整个数组可以使用数组名来表示,所以可以使用data来表示整个数组
//即:pc->data === data ,,,数组名在第一个参数中没有&和sizeof,代表的是数组首元素的地址,第二个参数是把
//每一个字节都设置成0,十进制的0,转为十六进制表示形式就是0x 00,,pc->data === data,数组名单独放在
//sizeof中,代表的是整个数组,计算的是整个数组的大小,单位是字节也可以写成:MAX * sizeof(Contact)
}
通过memset()内存函数初始化通讯录中的信息,同时将数组元素个数变量sz设置为0。
3.5?增加通讯录联系人信息部分:
//增加联系人的信息
void AddContact(Contact* pc)
{
//通讯录满员
if (pc->sz == MAX)
{
printf("通讯录已满、无法添加新的成员\n");
return; //返回类型是void,也可以写return,但是不能带出去返回值。
}
//通讯录未满,可以添加新成员,增加一个人的信息
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
(pc->sz)++;
printf("增加联系人员成功\n");
//由于[ ]的优先级高于->,所以,data[pc->sz]先进行结合,这个整体代表的就是数组下标为sz的元素,所以可以理解成
//pc指向了一个变量,而不是一个数组,而且,数组的本质上就可以理解成多个相同类型的元素的集合,即,可以理解成
//拆分的情况,那么,就把该数组中所有的元素相当于是拆分了出来放在了结构体成员变量中,所以,指针pc是有能力直接
//指向数组中的某一个元素的。
}
3.6?显示联系人信息的部分:
//显示联系人的信息
void PrintContact(const Contact* pc)
{
//打印出所有人的信息,即sz个人的信息。
int i = 0;
//打印标题
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-50s\n", "名字", "年龄", "性别", "电话", "地址");// \t === tab
//打印数据
for (i = 0; i < (pc->sz); i++)
{
printf("%-10s\t%-5d\t%-5s\t%-12s\t%-50s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
//通过pc->data[i]找到数组data中下标为i的元素,即找到了一个变量,再通过点来访问该人的姓名等,,由于姓名是一个数组,找到的就是整个数组,整个
//数组又可以使用数组名来表示,即:pc->data[i].name === name ,没有sizeof和&,代表数组首元素的地址,除了年龄是一个变量,其他的都是数组,和name同理。
}
3.7 删除联系人信息的部分:
//因为该函数只是为了满足删除,查找,修改功能的需要,而这三个功能对应的函数的实现都会在该 .c 文件内进行实现,所以,对于这个函数
//只需要在该 .c 文件内执行即可,不许要暴露给别人,,所以,在前面加上static,就固定了该函数只在目前所在的 .c 文件内进行工作即可。
//static 修饰函数,本质上是改变了函数的链接属性。
static int Find_By_Name(const Contact* pc,char name[])//数组形式接收,数组形式接受的话就不考虑const的使用了。
{
int i = 0;
for (i = 0; i < pc -> sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)//相等
//第一个参数先找到整个数组,可以使用数组名来表示,不是特例,即代表数组首元素的地址,第二个参数也不是特例,也是数组首元素的地址。
{
return i;
}
}
return -1;
}
//删除联系人信息
void DelContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空、不可以再进行删除操作\n");
return;
}
//删除某个人的信息
printf("请输入要删除人员的姓名:>");
scanf("%s", name);
//1、查找要删除的人
//不管是删除还是查找还是修改,都需要使用到查找这个功能,所以就单独把该功能拿出来封装一个函数;
int pos=Find_By_Name(pc,name);//一级指针传参和数组名传参
//不存在该人
if (pos == -1)
{
printf("要删除的人员不存在\n");
return;
}
//2、存在该人员,要进行删除,把数组该位置上的人员删除之后,数组后面的人员依次往前移动一个位置。
int i = 0;
for (i = pos; i < (pc->sz - 1); i++)
{
pc->data[i] = pc->data[i + 1];
}
//pc->sz -= 1;
pc->sz--;
//如果想删除最后一个,是删除不掉的,因为,如果10个元素,最后一个下标为9,判断条件是<9,,所以不进入循环,但是
//循环后面还有pc->sz--,,成员个数少了1,再显示人员信息的时候,访问不到最后一个人员了,,即使没删掉,也访问不掉,
//最后的结果和删掉最后一个人员的效果是一样的。
//假设MAX=3,把最后一个元素删除,本质上并没有从数组中删除掉,而是因为sz减1,打印的时候不访问最后一个元素,看起来和删除的效果是一样的,现在由于
//看起来删了,本质上没删去,如果再添加新元素会怎么样呢?
//因为删除完之后,元素个数就会减去1,由原来的3变成了2,,再添加新元素的时候,就会直接把新元素的内容放在下标为2的位置上,这样的话,即使之前的最后一个元素
//没删去,也会被新的元素覆盖掉,添加完之后元素个数加1,再打印出来就是添加成员后的信息,是对的。
printf("删除联系人员成功\n");
}
3.8?查找联系人信息的部分:
//查找联系人信息
void SearchContact(const Contact* pc)
{
char name[MAX_NAME] = { 0 };
//查找某个人的信息
printf("请输入要查找人员的姓名:>");
scanf("%s", name);
int pos = Find_By_Name(pc, name);
//要查找的人员不存在
if (pos == -1)
{
printf("要查找的人员不存在\n");
return;
}
else
{
//2、存在该人员,找出之后并打印出该成员的信息
//打印标题
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-50s\n", "名字", "年龄", "性别", "电话", "地址");// \t === tab
//打印数据
printf("%-10s\t%-5d\t%-5s\t%-12s\t%-50s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
3.9?修改联系人信息的部分:
//修改指定联系人的信息
void ModifyContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
//修改某个人的信息
printf("请输入要修改人员的姓名:>");
scanf("%s", name);
int pos = Find_By_Name(pc, name);
//要修改的人员不存在
if (pos == -1)
{
printf("要修改的人员不存在\n");
return;
}
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].addr);
printf("请输入修改后人员的电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改联系人员信息成功\n");
}
}
3.10?排序联系人信息的部分:
enum Option_qsort
{
exit_qsort,
name,
age,
sex,
addr,
tele
};
void menu_qsort()
{
printf("*****************************\n");
printf("***** 1、name 2、age *****\n");
printf("***** 3、sex 4、addr *****\n");
printf("***** 5、tele 0、exit_sort*\n");
printf("*****************************\n");
}
//按照名字进行排序
int Conpare_ByName(const void*e1, const void* e2)
{
return strcmp( ((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name );
}
//按照年龄进行排序
int Conpare_ByAge(const void*e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
//按照性别进行排序
int Conpare_BySex(const void*e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex);
}
//按照地址进行排序
int Conpare_ByAddr(const void*e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr);
}
//按照电话进行排序
int Conpare_ByTele(const void*e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
void print(Contact* pc)
{
int i = 0;
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-50s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-10s\t%-5d\t%-5s\t%-12s\t%-50s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
//排序联系人的信息
void SortContact(Contact* pc)
{
int input = 0;
do{
menu_qsort();
printf("请选择排序的方式:>");
scanf("%d", &input);
switch (input)
{
//按照名字进行排序
case name:
//这里求数组元素个数的时候,不要使用整个数组的大小比上数组首元素的大小,因为我们在这里排序的时候,仅对已添加的成员进行排序,比如添加的成员个数是3的话,
//就对这3个元素进行排序,如果开辟的数组有1000个元素,但是只添加3个的话,如果使用整个数组的大小比上数组首元素的大小,求出来的就是1000,意思是对1000个元素进行排序
//这样是不对的,应该对添加进去的3个元素进行排序,pc->sz,这个整体就已经代表了元素个数是3,不需要再计算了,所以,使用下面的代码即可。
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByName);
print(pc);
break;
//按照年龄进行排序
case age:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByAge);
print(pc);
break;
//按照性别进行排序
case sex:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_BySex);
print(pc);
break;
//按照地址进行排序
case addr:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByAddr);
print(pc);
break;
//按照电话进行排序
case tele:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByTele);
print(pc);
break;
//退出排序
case exit_qsort:
printf("退出排序\n");
printf("\n");
break;
//选择错误
default:
printf("选择排列方式错误,请重新进行选择:>\n");
break;
}
}while (input);
}
要知道库函数qsort的使用规则,上述代码可以实现多次排序,并且可以按照不同的要求进行排序,要注意的是,不同类型的内容进行排序的时候,两个元素的比较方式不同,要写出不同的代码。
3.11?清空所有联系人信息的部分:
//清空所有联系人的信息
void ClearContact(Contact* pc)
{
(pc->sz) = 0;
memset(pc->data, 0, sizeof(pc->data));
printf("清空成功\n");
printf("\n");
//在此过程把sz置为了0,再次打印的时候,不进入遍历的循环,不打印数据,直接出程序。
}
与初始化通讯录类似,使用memset内存设置函数,并把数组元素个数设为0,值得注意的是:在此过程把sz置为了0,再次打印的时候,不进入遍历的循环,不打印数
据,直接出程序,所以效果和清空所有联系人的效果一样、
四、代码以文件进行呈现:
4.1?test.c源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//通讯录
//要求:能够存放1000个联系人的信息
//每个人的信息包含:名字、年龄、性别、电话、地址、
//增加人的信息、删除人的信息、修改指定人的信息、查找指定人的信息、排序通讯录的信息、清除所有信息、显示联系人信息。
enum Option//枚举成员变量从0开始,依次递增1;
{
//枚举中的变量一般都采用大写。
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SORT,
PRINT,
CLEAR
};
void menu()
{
printf("*********************************\n");
printf("****** 1、add 2、del ******\n");
printf("****** 3、search 4、modify ****\n");
printf("****** 5、sort 6、print******\n");
printf("****** 7、clear 0、exit *****\n");
printf("*********************************\n");
}
int main()
{
int input = 0;
//创建通讯录,结构体类型在头文件中定义的,所以要引头文件,MAX的定义在头文件或者该源文件内定义都是可以的,因为包含了头文件
//但是如果把数组和数组元素个数sz定义成一个结构体,就要去头文件中定义,那边就需要MAX,如果MAX在test.c中定义的话,contact.h中就会报错
//既然这样,就直接把MAX定义在contact.h中去,即可。
//PeoInfo data[MAX] = { 0 };//不完全初始化
通讯录中当前有几个元素:
//int sz = 0;
//创建通讯录
Contact con; //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 PRINT:
//虽然只是打印信息,不会改变实参的信息,但是考虑的效率的话,还是使用传址调用比较好,结构体传参最好传地址。
PrintContact(&con);
break;
//清空所有联系人的信息
case CLEAR:
ClearContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新进行选择:>");
break;
}
} while (input);
//如果有同名的,一律操作第一个出现该名字的那个成员,因为遍历是从前往后遍历的,在这里不考虑同名的情况。
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//初始化通讯录函数
void InitContact(Contact* pc)
{
(pc->sz) = 0;
//内存设置函数 — memset() - 内存设置
memset(pc->data, 0, sizeof(pc->data));
//pc->data 就相当于找到了整个数组,而整个数组可以使用数组名来表示,所以可以使用data来表示整个数组
//即:pc->data === data ,,,数组名在第一个参数中没有&和sizeof,代表的是数组首元素的地址,第二个参数是把
//每一个字节都设置成0,十进制的0,转为十六进制表示形式就是0x 00,,pc->data === data,数组名单独放在
//sizeof中,代表的是整个数组,计算的是整个数组的大小,单位是字节也可以写成:MAX * sizeof(Contact)
}
//增加联系人的信息
void AddContact(Contact* pc)
{
//通讯录满员
if (pc->sz == MAX)
{
printf("通讯录已满、无法添加新的成员\n");
return; //返回类型是void,也可以写return,但是不能带出去返回值。
}
//通讯录未满,可以添加新成员,增加一个人的信息
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].addr);
printf("请输入电话:>");
scanf("%s", pc->data[pc->sz].tele);
(pc->sz)++;
printf("增加联系人员成功\n");
//由于[ ]的优先级高于->,所以,data[pc->sz]先进行结合,这个整体代表的就是数组下标为sz的元素,所以可以理解成
//pc指向了一个变量,而不是一个数组,而且,数组的本质上就可以理解成多个相同类型的元素的集合,即,可以理解成
//拆分的情况,那么,就把该数组中所有的元素相当于是拆分了出来放在了结构体成员变量中,所以,指针pc是有能力直接
//指向数组中的某一个元素的。
}
//显示联系人的信息
void PrintContact(const Contact* pc)
{
//打印出所有人的信息,即sz个人的信息。
int i = 0;
//打印标题
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-50s\n", "名字", "年龄", "性别", "电话", "地址");// \t === tab
//打印数据
for (i = 0; i < (pc->sz); i++)
{
printf("%-10s\t%-5d\t%-5s\t%-12s\t%-50s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
//通过pc->data[i]找到数组data中下标为i的元素,即找到了一个变量,再通过点来访问该人的姓名等,,由于姓名是一个数组,找到的就是整个数组,整个
//数组又可以使用数组名来表示,即:pc->data[i].name === name ,没有sizeof和&,代表数组首元素的地址,除了年龄是一个变量,其他的都是数组,和name同理。
}
//因为该函数只是为了满足删除,查找,修改功能的需要,而这三个功能对应的函数的实现都会在该 .c 文件内进行实现,所以,对于这个函数
//只需要在该 .c 文件内执行即可,不许要暴露给别人,,所以,在前面加上static,就固定了该函数只在目前所在的 .c 文件内进行工作即可。
//static 修饰函数,本质上是改变了函数的链接属性。
static int Find_By_Name(const Contact* pc,char name[])//数组形式接收,数组形式接受的话就不考虑const的使用了。
{
int i = 0;
for (i = 0; i < pc -> sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0)//相等
//第一个参数先找到整个数组,可以使用数组名来表示,不是特例,即代表数组首元素的地址,第二个参数也不是特例,也是数组首元素的地址。
{
return i;
}
}
return -1;
}
//删除联系人信息
void DelContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
if (pc->sz == 0)
{
printf("通讯录为空、不可以再进行删除操作\n");
return;
}
//删除某个人的信息
printf("请输入要删除人员的姓名:>");
scanf("%s", name);
//1、查找要删除的人
//不管是删除还是查找还是修改,都需要使用到查找这个功能,所以就单独把该功能拿出来封装一个函数;
int pos=Find_By_Name(pc,name);//一级指针传参和数组名传参
//不存在该人
if (pos == -1)
{
printf("要删除的人员不存在\n");
return;
}
//2、存在该人员,要进行删除,把数组该位置上的人员删除之后,数组后面的人员依次往前移动一个位置。
int i = 0;
for (i = pos; i < (pc->sz - 1); i++)
{
pc->data[i] = pc->data[i + 1];
}
//pc->sz -= 1;
pc->sz--;
//如果想删除最后一个,是删除不掉的,因为,如果10个元素,最后一个下标为9,判断条件是<9,,所以不进入循环,但是
//循环后面还有pc->sz--,,成员个数少了1,再显示人员信息的时候,访问不到最后一个人员了,,即使没删掉,也访问不掉,
//最后的结果和删掉最后一个人员的效果是一样的。
//假设MAX=3,把最后一个元素删除,本质上并没有从数组中删除掉,而是因为sz减1,打印的时候不访问最后一个元素,看起来和删除的效果是一样的,现在由于
//看起来删了,本质上没删去,如果再添加新元素会怎么样呢?
//因为删除完之后,元素个数就会减去1,由原来的3变成了2,,再添加新元素的时候,就会直接把新元素的内容放在下标为2的位置上,这样的话,即使之前的最后一个元素
//没删去,也会被新的元素覆盖掉,添加完之后元素个数加1,再打印出来就是添加成员后的信息,是对的。
printf("删除联系人员成功\n");
}
//查找联系人信息
void SearchContact(const Contact* pc)
{
char name[MAX_NAME] = { 0 };
//查找某个人的信息
printf("请输入要查找人员的姓名:>");
scanf("%s", name);
int pos = Find_By_Name(pc, name);
//要查找的人员不存在
if (pos == -1)
{
printf("要查找的人员不存在\n");
return;
}
else
{
//2、存在该人员,找出之后并打印出该成员的信息
//打印标题
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-50s\n", "名字", "年龄", "性别", "电话", "地址");// \t === tab
//打印数据
printf("%-10s\t%-5d\t%-5s\t%-12s\t%-50s\n",
pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
//修改指定联系人的信息
void ModifyContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
//修改某个人的信息
printf("请输入要修改人员的姓名:>");
scanf("%s", name);
int pos = Find_By_Name(pc, name);
//要修改的人员不存在
if (pos == -1)
{
printf("要修改的人员不存在\n");
return;
}
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].addr);
printf("请输入修改后人员的电话:>");
scanf("%s", pc->data[pos].tele);
printf("修改联系人员信息成功\n");
}
}
enum Option_qsort
{
exit_qsort,
name,
age,
sex,
addr,
tele
};
void menu_qsort()
{
printf("*****************************\n");
printf("***** 1、name 2、age *****\n");
printf("***** 3、sex 4、addr *****\n");
printf("***** 5、tele 0、exit_sort*\n");
printf("*****************************\n");
}
//按照名字进行排序
int Conpare_ByName(const void*e1, const void* e2)
{
return strcmp( ((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name );
}
//按照年龄进行排序
int Conpare_ByAge(const void*e1, const void* e2)
{
return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
//按照性别进行排序
int Conpare_BySex(const void*e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->sex, ((PeoInfo*)e2)->sex);
}
//按照地址进行排序
int Conpare_ByAddr(const void*e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->addr, ((PeoInfo*)e2)->addr);
}
//按照电话进行排序
int Conpare_ByTele(const void*e1, const void* e2)
{
return strcmp(((PeoInfo*)e1)->tele, ((PeoInfo*)e2)->tele);
}
void print(Contact* pc)
{
int i = 0;
printf("%-10s\t%-5s\t%-5s\t%-12s\t%-50s\n", "名字", "年龄", "性别", "电话", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-10s\t%-5d\t%-5s\t%-12s\t%-50s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
//排序联系人的信息
void SortContact(Contact* pc)
{
int input = 0;
do{
menu_qsort();
printf("请选择排序的方式:>");
scanf("%d", &input);
switch (input)
{
//按照名字进行排序
case name:
//这里求数组元素个数的时候,不要使用整个数组的大小比上数组首元素的大小,因为我们在这里排序的时候,仅对已添加的成员进行排序,比如添加的成员个数是3的话,
//就对这3个元素进行排序,如果开辟的数组有1000个元素,但是只添加3个的话,如果使用整个数组的大小比上数组首元素的大小,求出来的就是1000,意思是对1000个元素进行排序
//这样是不对的,应该对添加进去的3个元素进行排序,pc->sz,这个整体就已经代表了元素个数是3,不需要再计算了,所以,使用下面的代码即可。
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByName);
print(pc);
break;
//按照年龄进行排序
case age:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByAge);
print(pc);
break;
//按照性别进行排序
case sex:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_BySex);
print(pc);
break;
//按照地址进行排序
case addr:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByAddr);
print(pc);
break;
//按照电话进行排序
case tele:
qsort(pc->data, pc->sz, sizeof(pc->data[0]), Conpare_ByTele);
print(pc);
break;
//退出排序
case exit_qsort:
printf("退出排序\n");
printf("\n");
break;
//选择错误
default:
printf("选择排列方式错误,请重新进行选择:>\n");
break;
}
}while (input);
}
//清空所有联系人的信息
void ClearContact(Contact* pc)
{
(pc->sz) = 0;
memset(pc->data, 0, sizeof(pc->data));
printf("清空成功\n");
printf("\n");
//在此过程把sz置为了0,再次打印的时候,不进入遍历的循环,不打印数据,直接出程序。
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//类型的定义、通讯录中要放1000个人的信息,每个人的信息要包括名字、年龄、性别、电话、地址等,所以要定义成一个结构体,而该结构体要在两个源文件中使用,所以最好定义在头文件中,这样只需要包含一下头文件就可以频繁的使用了。
//定义结构体类型:
#define MAX_NAME 20 //全大写
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30
#define MAX 1000
typedef struct PeoInfo
{
//char name[20];
//直接写成固定值就写死了,不方便后期的修改,所以使用#define 来定义一个常量,后期直接改变#define中的内容即可。
char name[MAX_NAME];
//同上,性别也使用#define来定义
char sex[MAX_SEX];
int age;
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;//使用typedef对结构体类型重命名为:PeoInfo
//结构体嵌套
//通讯录
typedef struct Contact //重命名
{
PeoInfo data[MAX];//存放添加进去的联系人的信息
int sz;//记录当前通讯录中有效信息的个数
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人的信息
void AddContact(Contact* pc);
//显示联系人的信息
void PrintContact(const Contact* pc);
//const放在*的左边,指的就是指针变量pc指向的内容不可以被修改,pc指向了通讯录con,所以通讯录中的内容,即数组和sz都不可以发生改变,又因为联系人
//的每个信息都是数组中每一个元素的子集,现要求整个数组都不能改变,所以每个元素中的内容就更不能改变。
//删除联系人信息
void DelContact(Contact* pc);
//查找联系人的信息
void SearchContact(const Contact* pc);
//修改指定联系人
void ModifyContact(Contact* pc);
//排序联系人的信息
void SortContact(Contact* pc);
//如果在*左边加const,那么pc指向的内容就不能改变,pc指向了一个数组和sz,其中,排序的话,sz的个数没有变化, 但是,数组各元素之间会有排序,这个顺序就会导致
//数组的内容发生了改变,所以,不可以加const。
//清空所有联系人的信息
void ClearContact(Contact* pc);
通讯录的基本功能已经实现,如果对您有帮助,记得点赞收藏,谢谢各位~
|