用C语言制作一个拥有添加,删除,查找,修改,排序,清空,打印功能的通讯录
用分模块的方法来实现这个通讯录,函数的声明,类型的声明放在一个.h文件中,函数的实现放在一个.c文件中,在主文件中来调用函数。
首先创建一个test.c文件用来测试并且实现通讯录的功能
文章图片
通讯录需要显示一个菜单来让用户具体选择哪个功能,那么在test.c文件中就需要打印出来一个菜单,如下代码:
#include void menu()
{
printf("***********************************\n");
printf("******1.add2.del******\n");
printf("******3.search4.modify******\n");
printf("******5.sort6.empty******\n");
printf("******7.print0.exit******\n");
printf("***********************************\n");
}int main()
{
//通讯录:1.add 2.del 3.search 4,modify 5.sort 6.empty 7.print 0.exit
do
{menu();
} while (0);
return 0;
}
接着就要让用户选择输入某个数组来使用通讯录的功能,这时就可以用一个switch开关来实现了,如下代码:
int input = 0;
do
{
menu();
printf("请选择功能>:");
scanf("%d", &input);
switch (input)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 0:
break;
default:
printf("请重新输入\n");
break;
}
} while (input);
在while循环的条件判断部分直接写input,这样选择输入0的时候就直接跳出循环了,输入其他数字可以继续使用通讯录的功能。
case后面跟着的都是数字,有时候调试代码的时候比较容易忘记这个数字表示的是什么意思,所以这个时候可以用创建一个枚举类型来定义对应的枚举常量。
创建一个contact.h文件来放类型的定义,函数的声明。
文章图片
然后把枚举类型在里面进行声明
enum input
{
Exit,
add,
sub,
search,
modify,
sort,
empty,
print
};
之后要在主文件中调用这个头文件
#include "contact.h"
【c语言|通讯录的思路讲解和代码实现】同时可以把#include 这些库函数的引用都放到contact.h这个文件当中,这样要引用新的库函数时只需要在contact.h这个文件中添加就可以了。
这样switch开关中的代码就可以改成这样了:
switch (input)
{
case add:
break;
case sub:
break;
case search:
break;
case modify:
break;
case sort:
break;
case empty:
break;
case print:
break;
case Exit:
break;
}
然后开始用函数去实现每一个函数的功能,首先是添加功能,在添加之前,还得需要一个空的通讯录,来存放添加进去的通讯人的数据,而通讯人的数据有姓名,年龄,性别,电话号码,地址这些数据,都是一些不同类型的值,这时可以声明一个结构体类型来包含这些成员
好,切到contact.h文件来声明一个结构体类型:
typedef struct People
{
char name[20];
int age;
char sex[5];
char phone[20];
char address[30];
} People;
同时还对这个类型重命名了一下,方便后续使用这个结构体类型,这是需要考虑到通讯录存放数据多少的问题,如果address里面存放的内容放不下了,这是就需要对数组的大小进行更改,那这时就要先找到这个结构体类型才能更改了,如果代码写的比较多的话就会比较乱,所以可以用#define定义的标识符常量来定义这些值,以后要改的话就只需要找到#define就可以了
如下代码:
#define NameMax 20
#define SexMax 5
#define PhoneMax 20
#define AddressMax 30
typedef struct People
{
char name[NameMax];
int age;
char sex[SexMax];
char phone[PhoneMax];
char address[AddressMax];
} People;
因为这里是不同类型的值的定义,所以用#define来定义而不用枚举类型来定义,因为枚举类型是用来列举相同类型的值的,比如前面的通讯录功能都是属于通讯录的功能一类的值
下一步,通讯录不只存放一个人的数据,所以需要用这个结构体类型来创建一个数组,假设这个通讯录可以存放1000个人的数据,同时还能记录其中已存放了多少个人
那么可以在主函数中这么写:
int main()
{
//通讯录:1.add 2.del 3.search 4,modify 5.sort 6.empty 7.print 0.exit
People contact[1000];
int num;
对于这个通讯录和这个通讯人的数量,这两个变量可以两者之间是由联系的,那么此时就可以再声明一个结构体类型来包含这两个成员。
切到contact.h文件:
typedef struct Contact
{
People people[1000];
int num;
} Contact;
声明好这个通讯录类型之后,需要在主文件中用这个类型创建一个变量,同时对变量的内容进行初始化
int main()
{
//通讯录:1.add 2.del 3.search 4,modify 5.sort 6.empty 7.print 0.exit
Contact contact;
InitialContact(&contact);
切到contact.h文件声明函数:
//函数的声明//通讯录初始化
void InitialContact(Contact* pc);
此时创建一个contact.c文件来实现函数的内容:
文章图片
切到contact.c文件:
#include "contact.h"void InitialContact(Contact* pc)
{
memset(pc, 0, sizeof(*pc));
}
要记得引这个contact.h头文件
在对通讯录的内容进行完初始化之后,可以开始往里面添加通讯人的信息了,再声明一个函数:
test.c:
case add:
printf("添加通讯人\n");
AddPeople(&contact);
break;
contact.h:
//添加通讯人
void AddPeople(Contact* pc);
contact.c:
void AddPeople(Contact* pc)
{
if (pc->num == 1000)
{
printf("通讯录已满\n");
return;
}
printf("请输入姓名>:");
scanf("%s", pc->people[pc->num].name);
printf("请输入年龄>:");
scanf("%d", &pc->people[pc->num].age);
printf("请输入性别>:");
scanf("%s", pc->people[pc->num].sex);
printf("请输入电话>:");
scanf("%s", pc->people[pc->num].phone);
printf("请输入地址>:");
scanf("%s", pc->people[pc->num].address);
pc->num++;
}
输入完成之后,要记得让通讯人数量+1
接下来可以尝试显示一下通讯录中的通讯人信息了
contact.h:
//显示通讯人信息
void PrintContact(const Contact* pc);
contact.c:
void PrintContact(const Contact* pc)
{
printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
int i = 0;
for (i = 0;
i < pc->num;
i++)
{
printf("%-20s %-20d %-20s %-20s %-20s\n", pc->people[i].name, pc->people[i].age, pc->people[i].sex, pc->people[i].phone, pc->people[i].address);
}
}
在前面打印个通讯人列表修饰一下
test.c:
case print:
printf("显示通讯人信息\n");
PrintContact(&contact);
break;
接下来,开始实现通讯录的删除功能:
可以通过让用户输入某个人的名字来删除这个人在通讯录中的信息,删除功能的实现分为两步,第一步,是需要找到这个人,第二步,删除这个人在通讯录中的信息
contact.h:
//删除通讯人信息
void SubPeople(Contact* pc);
contact.c:
int FindByName(Contact* pc, const char* name)
{
//遍历每一个contact结构体中people数组成员中的已存放数据的每一个结构体
int i = 0;
for (i = 0;
i < sizeof(pc->num);
i++)
{
if (strcmp(pc->people[i].name, name) == 0)
{
return i;
//找到了返回对应结构体的下标
}
}
return -1;
//找不到就返回-1
}void SubPeople(Contact* pc)
{
if (pc->num == 0)//先判断通讯录中有无存储信息
{
printf("通讯录已清空\n");
return;
}
//1.找到
printf("请输入要删除的人的姓名;
>");
char name[NameMax];
scanf("%s", name);
int ret = FindByName(pc, name);
//用以接收放回的数组下标
if (ret == -1)
{
printf("找不到要删除的人\n");
return;
//让函数直接返回
}
//2.删除
//从找到的那个下标位置开始,后面的数组元素(通讯人结构体)依次往前赋值
int j = 0;
for (j = ret;
j < pc->num - 1;
j++)
{
pc->people[j] = pc->people[j + 1];
}
printf("删除成功\n");
pc->num--;
//通讯人数量-1
}
test.c:
case sub:
SubPeople(&contact);
break;
每完成一个功能,都要去尝试运行一下,看有没有bug,否则最后才去找bug可能会比较麻烦。
在删除功能中,用到了一个自己写的FindByName函数,然后在接下来的通讯录查找功能实现中,同样也要依赖这个函数,不仅如此,在通讯录修改功能中,也要用到这个函数,而这个函数的实现是为了删除、查找和修改功能而声明的,所以可以不用写在contact.h头文件中进行声明。
下面,来开始实现查找功能:
contact.h:
//查找通讯人
void SearchPeople(const Contact* pc);
contact.c:
void SearchPeople(const Contact* pc)
{
//查找
printf("请输入要查找的人的姓名;
>");
char name[NameMax];
scanf("%s", name);
int ret = FindByName(pc, name);
//用以接收放回的数组下标
if (ret == -1)
{
printf("找不到要查找的人\n");
return;
//让函数直接返回
}
//显示
printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s %-20d %-20s %-20s %-20s\n", pc->people[ret].name, pc->people[ret].age, pc->people[ret].sex, pc->people[ret].phone, pc->people[ret].address);
}
test.c:
case search:
SearchPeople(&contact);
break;
查找功能已经实现了,接下来就要实现修改功能了,先通过通讯人的姓名找到需要修改的结构体对应的下标,然后重新对这个结构体进行赋值就可以了
contact.h:
//修改通讯人信息
void ModifyPeople(Contact* pc);
contact.c:
void ModifyPeople(Contact* pc)
{
//查找
printf("请输入要修改的人的姓名;
>");
char name[NameMax];
scanf("%s", name);
int ret = FindByName(pc, name);
//用以接收放回的数组下标
if (ret == -1)
{
printf("找不到要修改的人\n");
return;
//让函数直接返回
}
//修改
printf("请输入要修改的姓名>:");
scanf("%s", pc->people[ret].name);
printf("请输入要修改的年龄>:");
scanf("%d", &pc->people[ret].age);
printf("请输入要修改的性别>:");
scanf("%s", pc->people[ret].sex);
printf("请输入要修改的电话>:");
scanf("%s", pc->people[ret].phone);
printf("请输入要修改的地址>:");
scanf("%s", pc->people[ret].address);
}
test.c:
case modify:
ModifyPeople(&contact);
break;
接下来可以开始实习通讯录的排序功能了,这里就按照年龄来进行一个排序,用到qsort这个函数,其中cmp函数需要自己去写
contact.h:
//排序
void SortByAge(Contact* pc);
contact.c:
int cmp(const void* e1, const void* e2)
{
Contact* p1 = (Contact*)e1;
Contact* p2 = (Contact*)e2;
if (p1->people[0].age > p2->people[1].age)
{
return 1;
}
else if (p1->people[0].age < p2->people[1].age)
{
return -1;
}
else
{
return 0;
}
}void SortByAge(Contact* pc)
{
if (pc->num == 0)
{
printf("通讯录已清空\n");
return;
}
int num = pc->num;
int width = sizeof(pc->people[0]);
qsort(pc->people, num, width, cmp);
}
test.c:
case sort:
printf("按照年龄排序\n");
SortByAge(&contact);
PrintContact(&contact);
break;
到这里通讯录的功能就只剩下清空了,可以用到一开始的初始化函数InitialContact
test.c:
case empty:
printf("清空通讯录\n");
InitialContact(&contact);
break;
现在通讯录的功能已经全部实现了
可是这个通讯录的功能还是有点不太好,就是通讯录默认的大小就是存放1000个人的数据,那么能不能创建一个可以随着添加人数的增加而变大的通讯录呢?鉴于最近学习了动态内存管理,所以对通讯录的功能做出了一点改进
对结构体类型的修改:
contact.h
typedef struct Contact
{
People* people;
int num;
int capacity;
//通讯录的容量
} Contact;
contact.c:
对InitialContact函数的修改:
void InitialContact(Contact* pc)
{
pc->people = (People*)calloc(3, sizeof(People));
if (pc->people == NULL)
{
perror("calloc");
return;
}
pc->num = 0;
pc->capacity = 3;
}
对AddPeople函数的修改:
void AddPeople(Contact* pc)
{
if (pc->num == pc->capacity)
{
pc->people = realloc(pc->people, (pc->capacity + 2) * sizeof(People));
if (pc->people == NULL)
{
perror("realloc");
return;
}
pc->capacity += 2;
}
printf("请输入姓名>:");
scanf("%s", pc->people[pc->num].name);
printf("请输入年龄>:");
scanf("%d", &pc->people[pc->num].age);
printf("请输入性别>:");
scanf("%s", pc->people[pc->num].sex);
printf("请输入电话>:");
scanf("%s", pc->people[pc->num].phone);
printf("请输入地址>:");
scanf("%s", pc->people[pc->num].address);
pc->num++;
}
对排序函数中cmp函数的修改:
int cmp(const void* e1, const void* e2)
{
People* p1 = (People*)e1;
People* p2 = (People*)e2;
if (p1->age > p2->age)
{
return 1;
}
else if (p1->age < p2->age)
{
return -1;
}
else
{
return 0;
}
}
test.c:
部分修改:
case empty:
printf("清空通讯录\n");
FreeContact(&contact);
break;
case Exit:
FreeContact(&contact);
break;
如下是完整的代码:
contact.h:
#include
#include
#include
#include //类型的声明
#define NameMax 20
#define SexMax 5
#define PhoneMax 20
#define AddressMax 30enum input
{
Exit,
add,
sub,
search,
modify,
sort,
empty,
print
};
typedef struct People
{
char name[NameMax];
int age;
char sex[SexMax];
char phone[PhoneMax];
char address[AddressMax];
} People;
typedef struct Contact
{
People* people;
int num;
int capacity;
//通讯录的容量
} Contact;
//函数的声明//通讯录初始化
void InitialContact(Contact* pc);
//添加通讯人
void AddPeople(Contact* pc);
//显示通讯人信息
void PrintContact(const Contact* pc);
//删除通讯人信息
void SubPeople(Contact* pc);
//查找通讯人
void SearchPeople(const Contact* pc);
//修改通讯人信息
void ModifyPeople(Contact* pc);
//排序
void SortByAge(Contact* pc);
//销毁通讯录
void FreeContact(Contact* pc);
contact.c:
#include "contact.h"void InitialContact(Contact* pc)
{
pc->people = (People*)calloc(3, sizeof(People));
if (pc->people == NULL)
{
perror("calloc");
return;
}
pc->num = 0;
pc->capacity = 3;
}void AddPeople(Contact* pc)
{
if (pc->num == pc->capacity)
{
pc->people = realloc(pc->people, (pc->capacity + 2) * sizeof(People));
if (pc->people == NULL)
{
perror("realloc");
return;
}
pc->capacity += 2;
}
printf("请输入姓名>:");
scanf("%s", pc->people[pc->num].name);
printf("请输入年龄>:");
scanf("%d", &pc->people[pc->num].age);
printf("请输入性别>:");
scanf("%s", pc->people[pc->num].sex);
printf("请输入电话>:");
scanf("%s", pc->people[pc->num].phone);
printf("请输入地址>:");
scanf("%s", pc->people[pc->num].address);
pc->num++;
}void PrintContact(const Contact* pc)
{
printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
int i = 0;
for (i = 0;
i < pc->num;
i++)
{
printf("%-20s %-20d %-20s %-20s %-20s\n", pc->people[i].name, pc->people[i].age, pc->people[i].sex, pc->people[i].phone, pc->people[i].address);
}
}int FindByName(Contact* pc, const char* name)
{
//遍历每一个contact结构体中people数组成员中的已存放数据的每一个结构体
int i = 0;
for (i = 0;
i < sizeof(pc->num);
i++)
{
if (strcmp(pc->people[i].name, name) == 0)
{
return i;
//找到了返回对应结构体的下标
}
}
return -1;
//找不到就返回-1
}void SubPeople(Contact* pc)
{
if (pc->num == 0)//先判断通讯录中有无存储信息
{
printf("通讯录已清空\n");
return;
}
//1.找到
printf("请输入要删除的人的姓名;
>");
char name[NameMax];
scanf("%s", name);
int ret = FindByName(pc, name);
//用以接收放回的数组下标
if (ret == -1)
{
printf("找不到要删除的人\n");
return;
//让函数直接返回
}
//2.删除
//从找到的那个下标位置开始,后面的数组元素(通讯人结构体)依次往前赋值
int j = 0;
for (j = ret;
j < pc->num - 1;
j++)
{
pc->people[j] = pc->people[j + 1];
}
printf("删除成功\n");
pc->num--;
//通讯人数量-1
}void SearchPeople(const Contact* pc)
{
//查找
printf("请输入要查找的人的姓名;
>");
char name[NameMax];
scanf("%s", name);
int ret = FindByName(pc, name);
//用以接收放回的数组下标
if (ret == -1)
{
printf("找不到要查找的人\n");
return;
//让函数直接返回
}
//显示
printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
printf("%-20s %-20d %-20s %-20s %-20s\n", pc->people[ret].name, pc->people[ret].age, pc->people[ret].sex, pc->people[ret].phone, pc->people[ret].address);
}void ModifyPeople(Contact* pc)
{
//查找
printf("请输入要修改的人的姓名;
>");
char name[NameMax];
scanf("%s", name);
int ret = FindByName(pc, name);
//用以接收放回的数组下标
if (ret == -1)
{
printf("找不到要修改的人\n");
return;
//让函数直接返回
}
//修改
printf("请输入要修改的姓名>:");
scanf("%s", pc->people[ret].name);
printf("请输入要修改的年龄>:");
scanf("%d", &pc->people[ret].age);
printf("请输入要修改的性别>:");
scanf("%s", pc->people[ret].sex);
printf("请输入要修改的电话>:");
scanf("%s", pc->people[ret].phone);
printf("请输入要修改的地址>:");
scanf("%s", pc->people[ret].address);
}int cmp(const void* e1, const void* e2)
{
People* p1 = (People*)e1;
People* p2 = (People*)e2;
if (p1->age > p2->age)
{
return 1;
}
else if (p1->age < p2->age)
{
return -1;
}
else
{
return 0;
}
}void SortByAge(Contact* pc)
{
if (pc->num == 0)
{
printf("通讯录已清空\n");
return;
}
int num = pc->num;
int width = sizeof(People);
qsort(pc->people, num, width, cmp);
}void FreeContact(Contact* pc)
{
free(pc->people);
pc->people = NULL;
pc->num = 0;
pc->capacity = 0;
}
test.c:
#include "contact.h"void menu()
{
printf("***********************************\n");
printf("******1.add2.del******\n");
printf("******3.search4.modify******\n");
printf("******5.sort6.empty******\n");
printf("******7.print0.exit******\n");
printf("***********************************\n");
}int main()
{
//通讯录:1.add 2.del 3.search 4,modify 5.sort 6.empty 7.print 0.exit //创建通讯录
Contact contact;
//初始化通讯录
InitialContact(&contact);
int input = 0;
do
{
menu();
printf("请选择功能>:");
scanf("%d", &input);
switch (input)
{
case add:
printf("添加通讯人\n");
AddPeople(&contact);
break;
case sub:
SubPeople(&contact);
break;
case search:
SearchPeople(&contact);
break;
case modify:
ModifyPeople(&contact);
break;
case sort:
printf("按照年龄排序\n");
SortByAge(&contact);
PrintContact(&contact);
break;
case empty:
printf("清空通讯录\n");
FreeContact(&contact);
break;
case print:
printf("显示通讯人信息\n");
PrintContact(&contact);
break;
case Exit:
FreeContact(&contact);
break;
default:
printf("请重新输入\n");
break;
}
} while (input);
return 0;
}
今后也会不定期更新
推荐阅读
- C语言|【C语言】动态内存管理详解
- c语言|C语言之动态内存管理
- c语言|C语言中的指针(进阶)
- c语言|C语言中的字符串函数和字符函数
- 数据库|C#对Access数据库的操作
- ADO.NET|C#数据库教程7-ADO.NET三层架构和数据库DBNull问题
- C# Winform 服务器和多个客户端通讯
- 注册表限制软件使用次数
- 数值算法|分段线性插值 C#代码