前言:之前,博主已经写过两个有意思的小项目:三子棋和扫雷,接下来,博主继续更新一个小项目-通讯录,包括3种版本,静态版,动态版,文件保存版。接下来,我们先讲解如何实现静态版。
目录
一.静态通讯录的概要
二.静态通讯录接口函数实现
1.静态通讯录的基本结构
2.游戏框架(供用户选择)
3.初始化通讯录
4.打印通讯录
5.增加成员
6.判断通讯录是否为空
7.查找函数
8.删除指定联系人
9.查找指定联系人
10.修改指定联系人
11.以名字排序联系人
12.以年龄排序联系人
13.清空通讯录
一.静态通讯录的概要
静态通讯录:使用的是定长数组,即数组的长度不能发生改变。我们可以设置通讯录可以记录的成员个数为1000个。
二.静态通讯录接口函数实现
文件名 | 功能 |
Contact.c | 通讯录函数接口的实现 |
Contact.h | 宏定义,头文件,接口函数的声明 |
test.c | 函数接口测试 |
1.静态通讯录的基本结构
通讯录是一个结构体。
包含:1.通讯录成员数组,每一个通讯录成员又是一个结构体,包括:地址,姓名,年龄,性别,地址,电话。
2.标志通讯录成员个数的变量。
注意:为了方便后序更改通讯录的各种大小,推荐使用宏定义
//注意宏定义后面不要跟分号!!!!不然会有问题
#define NAME_MAX 30 //名字最大长度
#define SEX_MAX 5 //性别最大长度
#define TELE_MAX 12 //电话最大长度
#define ADDR_MAX 30 //地址最大长度
#define MAX 1000 //通讯录数组的成员总数
typedef struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char addr[ADDR_MAX];
char tele[TELE_MAX];
}PeoInfo;
struct Contact //通讯录
{
PeoInfo data[MAX]; //结构体成员数组
int size; //标志通讯录中所含成员个数, 控制数组下标
};
2.游戏框架(供用户选择)
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
DESTORY,
};
void menu()
{
printf("******************************\n");
printf("**** 1. add 2. del *****\n");
printf("**** 3. search 4. modify****\n");
printf("**** 5. show 6. Destory ***\n");
printf("**** 0. exit **\n");
printf("******************************\n");
}
int main()
{
int input = 0;
struct Contact con; //创建通讯录,内含MAX个结构体成员
InitContact(&con); //初始化通讯录
do
{
menu();
printf("请选择->\n");
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 DESTORY:
DesContact(&con);
break;
case EXIT:
printf("退出成功!欢迎再次使用!\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
3.初始化通讯录
使用memset初始化通讯录成员数组
写法1:每个成员的大小*成员个数
void InitContact(struct Contact* ps)
{
assert(ps);
//最初没有数据
memset(ps->data, 0, sizeof(PeoInfo) * MAX); //通讯录成员数组初始化为0
ps->size = 0;//默认成员为0
}
写法2:数组单独放在sizeof内部->计算的是整个数组的大小
//写法2
void InitContact(struct Contact* ps)
{
memset(ps->data, 0, sizeof(ps->data));
//ps->data拿到了通讯录中PenInfo数组的数组名
//数组名单独放在sizeof内部,计算的是整个数组的大小
ps->size = 0;
}
4.打印通讯录
为了更直观,我们可以把标题也进行打印。通过遍历通讯录成员数组,即可把成员打印出来。
//这是右对齐 如果想要左对齐-> -%30d前面加符号 在%前面数字加负号
void ShowContact(struct Contact* ps)
{
int i = 0;
//打印标题
printf("%15s\t%5s\t%8s\t%15s\t%30s\t\n\n", "name", "age", "sex", "tele", "addr");
//打印数组中每个结构体成员的内容
for (i = 0; i < ps->size; i++)
{
printf("%15s\t%5d\t%8s\t%15s\t%30s\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr);
}
}
5.增加成员
写法1:直接对通讯录成员数组进行操作
void ADDContact(struct Contact* ps)
{
//先判断通讯录是否满了
if (ps->size == MAX)
{
printf("通讯录已满\n");
}
else
{
printf("请输入名字:");
scanf("%s", ps->data[ps->size].name); //数组名不用&
printf("请输入年龄: ");
scanf("%d", &(ps->data[ps->size].age));
printf("请输入地址:");
scanf("%s", ps->data[ps->size].addr);
printf("请输入号码:");
scanf("%s", ps->data[ps->size].tele);
printf("请输入性别:");
scanf("%s", ps->data[ps->size].sex);
printf("添加成功\n");
ps->size++; //添加了一个成员,size++
}
}
写法2:先创建一个临时结构体成员,然后赋值后,赋给通讯录结构体数组中ps->size位置
//增加成员的写法2:
void ADDContact(struct Contact* ps)
{
PeoInfo tmp = { 0 };
//先判断通讯录是否满了
if (ps->size == MAX)
{
printf("通讯录已满\n");
}
else
{
printf("请输入名字:");
scanf("%s", tmp.name); //数组名不用&
printf("请输入年龄: ");
scanf("%d", &(tmp.age));
printf("请输入地址:");
scanf("%s", tmp.addr);
printf("请输入号码:");
scanf("%s", tmp.tele);
printf("请输入性别:");
scanf("%s", tmp.sex);
ps->data[ps->size] = tmp;
printf("添加成功\n");
ps->size++;
}
}
6.判断通讯录是否为空
因为size 标志的是通讯录中的成员个数,所以只要判断size是否为0即可知道通讯录是否为空
//如果为空,判断成立,返回1
bool EmptyContact(struct Contact* ps)
{
return ps->size == 0;
}
7.查找函数
因为后续删除指定联系人,更改指定联系人的信息都需要进行查找,所以可以把查找函数单独封装
找到了,返回对应的下标,找不到返回-1
因为不作修改,所以可以加上const修饰
方法:遍历结构体成员数组进行查找
int FindContactByName(const struct Contact* ps,const char*name )
{
//遍历查找
int i = 0;
for(i = 0;i< ps->size;i++)
{
if( strcmp(name,ps->data[i].name) == 0)
{
//返回下标
return i;
}
}
return -1;
}
8.删除指定联系人
注意:删除联系人->从该位置开始后面的数据往前覆盖
如通讯录为空,则不删除,直接返回.
void DelContact(struct Contact* ps)
{
if (EmptyContact(ps))
{
printf("通讯录为空,无法删除\n");
return ;
}
char name[NAME_MAX] = { 0 }; //用来存储要删除的指定联系人的名字
printf("请输入要删除人得名字:>");
scanf("%s",name);
//查找
int pos = FindContactByName(ps, name);
if (pos == -1)
{
printf("指定的联系人不存在\n");
}
else
{
//删除操作
//从pos位置开始,后面的往前覆盖
int i = 0;
for (i = pos; i < ps->size - 1; i++)
{
ps->data[i] = ps->data[i + 1];
}
//删除了元素,size--
ps->size--;
printf("删除成功\n");
}
}
9.查找指定联系人
方法:输入要查找的联系人名字,调用查找函数进行查找,若找到,返回对应下标,就把该下标对应的联系人对应信息打印出来
void SearchContact(const struct Contact* ps)
{
char name[NAME_MAX] = {0};
printf("输入要查找的联系人:>");
scanf("%s",name);
int pos =FindContactByName(ps,name);
if(pos == -1)
{
printf("要查找的联系人不存在\n");
}
else
{
// 打印该联系人对应的信息
//打印标题
printf("%15s\t%5s\t%8s\t%15s\t%30s\t\n\n", "name", "age", "sex", "tele", "addr");
printf("%15s\t%5d\t%8s\t%15s\t%30s\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr);
}
}
}
10.修改指定联系人
方法:输入要查找的联系人名字,调用查找函数进行查找,找到了返回对应下标,然后修改对应数组下标的内容
void ModifyContact(struct Contact* ps)
{
char name[NAME_MAX] = { 0 };
printf("请输入要修改人名字:>");
scanf("%s", name);
int pos = FindContactByName(ps, name);
if (-1 == pos)
{
printf("查无此人\n");
}
else
{
printf("请输入新名字:");
scanf("%s", ps->data[pos].name); //数组名不用&
printf("请输入新年龄: ");
scanf("%d", &(ps->data[pos].age));
printf("请输入新地址:");
scanf("%s", ps->data[pos].addr);
printf("请输入新号码:");
scanf("%s", ps->data[pos].tele);
printf("请输入新性别:");
scanf("%s", ps->data[pos].sex);
}
}
11.以名字排序联系人
相当于字符串比较->使用strcmp
这里可以使用两种方法:冒泡排序或者qsort排序
关于qsort:qsort函数详解
比较的是通讯录成员数组中的成员名字!!!
void SortContact(struct Contact* pcon)
{
int i, j;
struct PeoInfo tmp;
for(i = 0; i < pcon->sz - 1; i++)
{
for(j = 0; j < pcon->sz - 1 - i; j++)
{
if(0 < strcmp(pcon->data[j].name, pcon->data[j + 1].name))
{
tmp = pcon->data[j];
pcon->data[j] = pcon->data[j + 1];
pcon->data[j + 1] = tmp;
}
}
}
}
void SortContactByName(struct Contact* ps)
{
qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_ContactByName);
}
int cmp_ContactByName(const void* e1, const void* e2)
{
return strcmp( ((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name);
}
//注意
// ((struct PeoInfo*)e1)->name 要括起来,
// (struct PeoInfo*)e1->name 这样写是错误的
12.以年龄排序联系人
注意排序的是通讯录里面的成员数组ps->data:每个元素的类型为struct PenInfo ( 结构体成员类型)要排序的个数为ps->size
size标志的是通讯录成员个数
比较函数:比较的是成员的年龄
void SortContact(struct Contact* ps)
{
qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_ContactbyAge);
}
int cmp_ContactbyAge(const void* e1, const void* e2)
{
return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
13.清空通讯录
清空通讯录:相当于堆通讯录重新初始化.
void ClearContact(Contact* pcon)
{
InitContact(pcon);
}
三.静态通讯录的缺点
成员有限:容易造成空间浪费/空间不够的情况。
为了解决这个问题,下一篇文章,博主将会带大家了解动态通讯录.
很感谢你能看到这里,~如果感觉对你有帮助,欢迎给博主一个三连呀~