C语言小项目——通讯录
功能介绍:
1.联系人信息:姓名+年龄+性别+地址+电话
2.通讯录中可以存放100个人的信息
3.功能:
1>增加联系人
2>删除指定联系人
3>查找指定联系人的信息
4>修改指定联系人的信息
5显示所有联系人的信息
6>排序(名字)
我们将采用模块化设计,分为三个模块:
1. test.c ——— 测试通讯录
2. contact.c ——— 通讯录的声明
3. contact.h ——— 函数的声明
首先,我们需要一个菜单,以及主函数选择功能的部分。
```test.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void menu()
{
printf("*******************欢迎来到你的通讯录*******************\n");
printf("**********1.添加联系人************2.删除联系人**********\n");
printf("**********3.查找联系人************4.修改联系人**********\n");
printf("**********5.显示联系人************6.联系人排序**********\n");
printf("**********0.退出通讯录*********************************\n");
printf("*******************欢迎来到你的通讯录*******************\n");
}
int main()
{
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 0:
printf("退出通讯录");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
```
- 在
main
函数中,使用do-while
循环不断调用menu
函数显示菜单,并使用scanf
函数获取用户的输入。然后通过switch
语句根据用户的输入执行相应的操作,如果输入为0
,则输出 “退出通讯录” 并结束程序;如果输入为其他值,则输出 “选择错误”。
那么通讯录中肯定需要人的信息,那么这些人的信息,我们可以把他封装成一个结构体。我们将这些类型信息放进头文件中,在这里我们将它放进已经创建好的contact.h中。
```contact.h
//人的信息
typedef struct Peopleinform
{
char name[40];
int age;
char sex[5];
char addr[20];
char telephone[20];
}Peopleinform;
```
在这里,我们定义了一个名为
Peopleinform
的结构体类型,用于存储人员的信息。我们通过使用typedef
关键字,可以为这个结构体类型定义一个别名Peopleinform
,这样在后续的代码中就可以直接使用Peopleinform
来声明该类型的变量,而不需要每次都写完整的struct Peopleinform
。
我们继续往下写,接下来我们创建通讯录,我们可以声明一个名为data
的数组,数组的元素类型为Peopleinform
结构体,当然我们在这里必须添加contact.h头文件才能使用它。代码部分如下:
```test.c
#include<stdio.h>
#include"contact.h"
/* --省略void menu部分--*/
int main()
{
int input = 0;
//创建通讯录
Peopleinform data[100];
do {
menu();
printf("请选择:>");
scanf("%d", &input);
/*-- 省略switch部分 --*/
} while (input);
return 0;
}
```
在我们的项目中,自己创建的头文件通常用双引号''"包裹起来,这样我们就可以在.c文件中使用它啦!当然,我们如果把这个通讯录存放人的信息的容量,以及可以显示的当前创建的人的信息封装起来,变成一个结构体。为了方便之后修改我们增加一个预处理器指令#define MAX 100
```contact.h
#define MAX 100
typedef struct Contact
{
Peopleinform data[MAX];//存放人的信息的容量
int sz;//当前存放信息的个数
}Contact;
```
那么我们如果想在主函数中使用,只需要创建一个名为con
的Contact
类型的对象。这样就可以使用con
来存储和操作通讯录相关的数据了。接着我们初始化通讯录。并在contact.h头文件中为他声明。
```test.c
int main()
{
int input = 0;
//创建通讯录
Contact con;//创建一个名为con的Contact对象
//初始化通讯录
InitContact(&con);
do {
menu();
printf("请选择:>");
scanf("%d", &input);
/*-- 省略switch部分 --*/
} while (input);
```
```contact.h
void InitContact(Contact *pc);
```
接下来,contact.c文件终于出场了,由于我们在contact.h头文件声明过void InitContact(Contact *pc); 接下来,我们就可以在contact.c直接使用它。
我们给这个Contact
结构体的函数初始化一下:
```contact.c
#include"contact.h"
void InitContact(Contact* pc)
{
pc->sz=0;
memset(pc->data,0,sizeof(pc->data));
}
```
这里我们选择使用memset函数进行初始化,
(注:memset
函数是 C 标准库中的一个函数,定义在<string.h>
头文件中。它的作用是将一段内存区域设置为指定的值。函数原型为void *memset(void *s, int c, size_t n)
,其中s
是指向要填充的内存块的指针,c
是要设置的值,n
是要填充的字节数。)
紧接着,我们在Switch()语句中,加入我们目前需要的语句,我们现在来写添加联系人模块。
添加联系人模块
```contact.h
//增加联系人
void AddContact(Contact* pc);
```
```contact.c
void AddContact(Contact* pc)
{
if (pc->sz == MAX)
{
printf("通讯录已满!无法添加!\n");
return;
}
//增加一个人的信息
printf("请输入名字:>");
scanf("%s", pc->data[pc->sz].name);
printf("请输入年龄:>");
scanf("%s", &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].telephone);
pc->sz++;
}
```
(注:在
InitContact
和AddContact
函数中,pc
用于访问和修改调用者提供的Contact
结构体实例。当调用这些函数时,传递的是Contact
结构体变量的地址,函数内部通过这个指针来操作外部传入的结构体。)
pc-sz++; 的作用
假设
sz
初始值为5
,执行pc->sz++;
后,sz
的值变为6
,表示通讯录中联系人的数量从5
个增加到了6
个。 这对于跟踪通讯录中实际存储的联系人数量非常重要,在后续的操作,如显示联系人列表、检查通讯录是否已满等功能中,都依赖这个计数。
显示联系人模块
显示联系人模块也是一样,我们现将函数声明放在头文件当中,
void ShowContact(const Contact* pc);
然后我们用一个for
循环遍历pc->data
数组,其中,pc->data
数组存储了所有联系人的信息,循环条件i < pc->sz
确保只遍历已存储联系人的部分,pc->sz
表示当前通讯录中实际存储的联系人数量。
```contact.c
void ShowContact(Contact* pc)
{
int i = 0;
printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名","年齡","性別","地址","電話");
for (i = 0; i < pc->sz; i++)
{
printf("%s\t%d\t%s\t%s\t%s\t",pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].telephone);
}
}
```
这样,我们就基本完成了这个显示联系人的设计啦!那么紧接着,我们如果想要删除这个指定的联系人,该怎么办呢?和上面一样,我们先在头文件中添加一个声明。
void DelContact(Contact* pc);
接着,我们在contact.c文件中去构建这个删除联系人模块,
删除联系人模块
void DelContact(Contact* pc)
{
if (pc->sz ==0)
{
printf("无可删除的联系人");
return;
}
//删除
//找到要删除的人
printf("请输入要删除的人的名字:");
scanf("%s", name);
int i = 0;
int del = 0;
for (i = 0; i<pc->sz; i++)
{
if(strcmp(pc->data[i].name,name))==0
{
del = i;
break;
}
//删除
for (i = 0; i < pc->sz; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功");
}
}
当我们通讯录里面没有联系人的时候,我们输出“无可删除的联系人”。接着,我们使用 for
循环遍历联系人列表,使用 strcmp
函数比较每个联系人的名字和输入的名字,如果相等则将该元素的索引存储在 del
中并跳出循环。
那么接下来,我们可以发现的是,无论是删除模块、查找模块和修改模块中,我们都需要先找到联系人,才能进行下一步的操作,既然都需要,我们似乎就可以把这个查找部分封装成一个函数,
// 根据姓名查找联系人在数组中的位置,如果找到返回对应索引,未找到返回 -1
int FINDBY_NAME(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0) // 比较字符串是否相等
{
return i; // 找到返回索引
}
}
return -1; // 未找到返回 -1
}
替换后代码如下:
// 删除联系人的函数
void DelContact(Contact* pc)
{
if (pc->sz == 0)
{
printf("没有联系人可删除\n");
return;
}
// 输入要删除的联系人姓名
char name[40] = { 0 };
printf("请输入要删除的联系人姓名: ");
scanf("%s", name);
int ret = FINDBY_NAME(pc, name);
if (ret == -1)
{
printf("要删除的联系人不存在\n");
return;
}
// 删除操作,将后面的元素依次往前移
for (int i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
我们来看查找联系人模块,
查找联系人模块
void SerContact(const Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("请输入要查找人的名字:");
scanf("%s",name);
int pos = FINDBY_NAME(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
//打印信息
printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名", "年齡", "性別", "地址", "電話");
printf("%s\t%-5d\t%-5s\t%-5s\t%-5s\t\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].telephone);
}
我们查找模块,首先输入要查找的联系人姓名,调用 FINDBY_NAME
函数查找该联系人的位置,如果不存在则提示联系人不存在并返回。否则,打印该联系人的详细信息。
紧接着,我们再来看修改联系人,
修改联系人模块
//修改联系人
void ReviContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("请输入要修改人的名字:");
scanf("%s", name);
int pos = FINDBY_NAME(pc, name);
if (-1 == pos)
{
printf("要修改的人不存在\n");
return;
}
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].telephone);
printf("修改完成\n");
}
我们想要修改联系人,首先肯定需要找到这个联系人,所以我们首先要查找,输入要修改的联系人姓名,调用FINDBY_NAME
函数可以查找该联系人的位置,如果不存在则提示联系人不存在并返回。否则,依次输入新的联系人信息。
排序联系人模块
最后一个模块,就是对这些联系人进行排序,这里使用使用冒泡排序算法,比较相邻联系人的姓名,如果前一个联系人的姓名大于后一个联系人的姓名,则交换它们的位置。
// 排序联系人(按姓名升序排序)
void SortContact(Contact* pc)
{
int i, j;
Peopleinform temp;
for (i = 0; i < pc->sz - 1; i++)
{
for (j = 0; j < pc->sz - i - 1; j++)
{
if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
{
temp = pc->data[j];
pc->data[j] = pc->data[j + 1];
pc->data[j + 1] = temp;
}
}
}
printf("联系人已按姓名排序\n");
}
strcmp
是 C 语言标准库 <string.h>
中的一个字符串比较函数,用于比较两个字符串的大小。
函数原型int strcmp(const char *str1, const char *str2);
在 SortContact
函数中,strcmp(pc->data[j].name, pc->data[j + 1].name) > 0
表示如果 pc->data[j].name
这个字符串在字典序上大于 pc->data[j + 1].name
这个字符串,就交换这两个联系人的信息,从而实现按姓名排序的功能。
源代码如下:
```contact.h
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME_MAX 40 // 定义 NAME_MAX 的大小
// 个人信息
typedef struct Peopleinform
{
char name[40];
int age;
char sex[5];
char addr[20];
char telephone[20];
}Peopleinform;
typedef struct Contact
{
Peopleinform data[MAX];// 存储个人信息的数组
int sz;// 当前存储信息的数量
}Contact;
// 初始化通讯录
void InitContact(Contact* pc);
// 添加联系人
void AddContact(Contact* pc);
// 删除联系人
void DelContact(Contact* pc);
// 查找联系人
void SerContact(const Contact* pc);
// 修改联系人
void ReviContact(Contact* pc);
// 排序联系人
void SortContact(Contact* pc);
// 显示联系人
void ShowContact(const Contact* pc);
```
```contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void InitContact(Contact* pc)
{
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
if (pc->sz == MAX)
{
printf("通讯录已满!无法添加!\n");
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].telephone);
pc->sz++;
}
// 根据姓名查找联系人在数组中的位置,如果找到返回对应索引,未找到返回 -1
int FINDBY_NAME(const Contact* pc, char name[])
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(pc->data[i].name, name) == 0) // 比较字符串是否相等
{
return i; // 找到返回索引
}
}
return -1; // 未找到返回 -1
}
// 删除联系人的函数
void DelContact(Contact* pc)
{
if (pc->sz == 0)
{
printf("没有联系人可删除\n");
return;
}
// 输入要删除的联系人姓名
char name[40] = { 0 };
printf("请输入要删除的联系人姓名: ");
scanf("%s", name);
int ret = FINDBY_NAME(pc, name);
if (ret == -1)
{
printf("要删除的联系人不存在\n");
return;
}
// 删除操作,将后面的元素依次往前移
for (int i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
pc->sz--;
printf("删除成功\n");
}
//查找联系人
void SerContact(const Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("请输入要查找人的名字:");
scanf("%s",name);
int pos = FINDBY_NAME(pc, name);
if (-1 == pos)
{
printf("要查找的人不存在\n");
return;
}
//打印信息
printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名", "年齡", "性別", "地址", "電話");
printf("%s\t%-5d\t%-5s\t%-5s\t%-5s\t\n", pc->data[pos].name,
pc->data[pos].age,
pc->data[pos].sex,
pc->data[pos].addr,
pc->data[pos].telephone);
}
//修改联系人
void ReviContact(Contact* pc)
{
char name[NAME_MAX] = { 0 };
printf("请输入要修改人的名字:");
scanf("%s", name);
int pos = FINDBY_NAME(pc, name);
if (-1 == pos)
{
printf("要修改的人不存在\n");
return;
}
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].telephone);
printf("修改完成\n");
}
// 排序联系人(按姓名升序排序)
void SortContact(Contact* pc)
{
int i, j;
Peopleinform temp;
for (i = 0; i < pc->sz - 1; i++)
{
for (j = 0; j < pc->sz - i - 1; j++)
{
if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
{
temp = pc->data[j];
pc->data[j] = pc->data[j + 1];
pc->data[j + 1] = temp;
}
}
}
printf("联系人已按姓名排序\n");
}
// 显示所有联系人信息的函数
void ShowContact(const Contact* pc)
{
int i = 0;
printf("%s\t%-5s\t%-5s\t%-5s\t%-5s\t\n", "姓名", "年龄", "性别", "地址", "电话");
for (i = 0; i < pc->sz; i++)
{
printf("%s\t%d\t%s\t%s\t%s\n", pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr,
pc->data[i].telephone);
}
}
```
```test.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void menu()
{
printf("*******************欢迎来到你的通讯录*******************\n");
printf("**********1.添加联系人************2.删除联系人**********\n");
printf("**********3.查找联系人************4.修改联系人**********\n");
printf("**********5.显示联系人************6.联系人排序**********\n");
printf("**********0.退出通讯录**********************************\n");
printf("*******************欢迎来到你的通讯录*******************\n");
}
int main()
{
int input = 0;
//创建通讯录
Contact con;//创建一个名为con的Contact对象
//初始化通讯录
InitContact(&con);
do {
menu();
printf("请选择:>");
if (scanf("%d", &input) != 1) {
// 处理输入错误的情况,例如提示用户重新输入
printf("输入错误,请重新输入\n");
// 清空输入缓冲区
while (getchar() != '\n');
continue;
}
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
SerContact(&con);
break;
case 4:
ReviContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 6:
SortContact(&con);
case 0:
printf("退出通讯录");
break;
default:
printf("选择错误\n");
break;
}
} while (input!=0);
}
```