C语言小项目通讯录——静态版
文章目录
1.前言:
大家好,今天又来和大家一起学习C语言了,今天我们来完成通讯录的静态版。简而言之就是这个通讯录的存储空间,存储的信息量时静态的不可变的。之后我还会给大家分享动态版的和文件版的。
2.思路解析:
在写代码之前,我们先想一想,一个通讯录它是用来干什么的,它有什么功能。
毫无疑问通讯录是用来存储联系人的信息的,而联系人的信息一般就包括了:姓名,性别,年龄,电话,住址等等。而通讯录的功能也有很多,比如你要添加联系人,就得有个添加功能,在下面的代码中我们用一个函数AddContact来实现。有添加就会有删除,就用DelContact实现。还有搜索,展示,修改,排序功能我们都分别用函数(SearchContact,showContact,ModifyContact,SortContact)来实现。
说完函数的功能,我们刚刚提到联系人的信息有很多,我们应该如何表示呢,这个时候结构体就派上用场了,struct。我们可以创建一个结构体

想图中所示,我们用结构体来存储名字,年龄,性别,电话,住址。这里我们可以看到这个结构体可以用来存储一个人的信息,而我们通讯录要存储很多人的信息,这时候,我们就应该在创建一个结构体,里面的内容包括一个结构体数组(每个元素都是上图中的PepInfo)和一个变量size用来记录已存储联系人的数量。

如上图所示,同时因为这篇文章讲的是静态版的通讯录,所以上述给的数组大小都是MAX什么什么,这是为了方便调节各个数组大小而设的,就是用宏定义来实现的。
#define MAX 1000
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 20
就像这样,只要改这里就可以修改所有这些数组出现的地方,就方便很多了。
ok,思路分析到这里就差不多了,接下来是代码实现。
3.代码实现
3-1.初始化函数
和之前的扫雷一样,写一个通讯录最好也是分模块来编写。也就是分为,Contact.h(用于通讯录各功能函数的声明和宏定义结构体定义),Contact.c(用于实现通讯录的各功能),test.c(用于菜单和测试程序,使用功能)。
首先我们来contact.h中定义上面说的结构体以及必要的宏定义(上三图所示)
接下来我们在主函数内创建一个结构体struct Contact con。然后因为我们是静态版的通讯录,所以我们创建的结构体相当于向内存申请了一个int变量和元素为max个struct pepinfo结构体数组。这时候我们就得对这些结构体进行初始化。所以就得写一个函数init contact来初始化。
因为要改变这些结构体,所以我们选择传址进去并对结构体进行初始化, 同时将size置为0表示现在还没存入联系人。


*同时记得在.h中声明一下。
接下来我们就可以去完成通讯录的各个功能了。
3-2.添加功能
这个功能非常简单,只需要先对size进行判断,看是否存储已满,满则退出程序因为通讯录已经不能容纳更多信息了,否则就输入联系人信息进行存储。最后size++记录联系人+1.
void AddContact(struct Contact *ps)
{
if (ps->size == MAX)
{
printf("通讯录已满\n");
return;
}
else
{
printf("请输入名字>:");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>:");
scanf("%d", &ps->data[ps->size].age);
printf("请输入性别>:");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>:");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>:");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
}
这样添加功能就完成了,然后我们来写删除代码。
3-3.删除功能
这里我们采用指定名字来删除。就先创建个数组存储名字信息然后用strcmp来搜索是否存在这么个联系人(这里这个通过名字来查找的功能其实在其他功能中也用得上,比如:搜素,修改等,这些功能你首先必须得确定是否有这么个联系人给你删除,修改或搜索),所以这里我们就可以封装一个函数专门用来以名字取锁定一个人具体实现如下
static int FindContactbyname(const struct Contact *ps,char name[MAX_NAME])
{
for (int i = 0; i < ps->size; i++)
{
if (0 == strcmp(name, ps->data[i].name))
{
return i;
}
}
return -1;
}
接下来就可以正式实现删除了
删除的逻辑也很简单就是从要删除的人处将结构体数组的后一位把前一位信息给覆盖然后size–一下记录删除一个联系人
void DelContact(struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要删除人的名字>:");
scanf("%s", name);
int pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("删除的对象不存在\n");
}
else
{
for (int j = pos; j < ps->size; j++)
{
ps->data[j] = ps->data[j + 1];
}
}
printf("删除成功\n");
ps->size--;
}
*同样记得去声明一下
这样就完成了删除功能。然后我们实现展示/打印功能,可以用来测试一下目前功能是否没毛病。
3-3.显示/打印功能
这个功能也是非常简单,只需要用个循环结构就能够把已经存储的联系人的各项信息打印在屏幕上。接下来我们来看看怎么实现的把!
void ShowContact(const struct Contact* ps)
{
if (ps->size == 0)
{
printf("通讯录暂为空\n");
return;
}
else
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\t\n","名字","年龄","性别","电话","地址");
for (int i = 0; i < ps->size; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\t\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr
);
}
}
这样做就可以将信息打印在屏幕上了,我们来测试一下吧。


这里我们首先调用ADD函数存储了一个联系人的信息然后调用show函数将它显示在屏幕上。
我们再看如果我们没有添加信息会怎样。

这里可以看到直接就提示通讯录为空了。
*同样记得去声明一下函数
3-4.查找功能
跟之前分析的一样我们通过名字来搜索一个人。调用我们之前写的findbyname函数进行查找,如果存在,则将此人信息打印出来给人看,否则结束。
void SearchContact(const struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要搜索人的名字\n");
scanf("%s", name);
int pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("查找的人不存在\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\t\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\t\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr
);
}
}
这样就可以找到我们想找的人了
功能测试:


*声明函数不要忘记
3-5.修改功能
不多废话,同样先调用findbyname函数进行此人的检索,看是否存在这么个人,然后对其信息进行修改
void ModifyContact(struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要修改人的名称\n");
scanf("%s", name);
int pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("要修改的人不存在\n");
}
else
{
printf("请输入名字>:\n");
scanf("%s", ps->data[pos].name);
printf("请输入年龄>:\n");
scanf("%d", &ps->data[pos].age);
printf("请输入性别>:\n");
scanf("%s", ps->data[pos].sex);
printf("请输入电话>:\n");
scanf("%s", ps->data[pos].tele);
printf("请输入地址>:\n");
scanf("%s", ps->data[pos].addr);
printf("修改成功\n");
}
}
这样就可以修改一个人的信息啦.
声明函数不要忘记.
3-6.排序功能
排序是这几个函数中相对复杂一些的,因为我们要给用户几种排序规则,比如:按名字,按年龄,按电话等,这时候我们就要用到一个好用的函数qsort了,他可以对任何数据类型进行排序,只要你给它传一个相应的比较函数.而之前我们又说要让用户可以选择,所以我们就可以使用转移表来实现一个这样的功能.具体代码如下
//按名字排序
const int* SortByName(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return strcmp(pc1->name, pc2->name);
}
//按年龄排序
const int* SortByAge(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return pc1->age - pc2->age;
}
//按电话排序
const int* SortByTele(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return strcmp(pc1->tele, pc2->tele);
}
//转移表,用函数指针数组实现.
int (*CompareFunctions[])(const void*, const void*) =
{
SortByName,//下表为0
SortByAge,
SortByTele
};
//函数主题
void SortContact(struct Contact* ps)
{
int choice;
printf("请选择排序规则\n");
printf("0.按名字排序\n1.按年龄排序\n2.按电话排序\n");
scanf("%d", &choice);
if (choice < 0 || choice >= 3)
{
printf("无效选择\n");
return 1;
}
qsort(ps->data,
ps->size,
sizeof(struct PepInfo),
CompareFunctions[choice]);//去sort函数,要引头文件#include<stdlib.h>
}
通过这样我们就可以实现排序功能了.由于篇幅限制.测试部分就略过了
3.7test.c中主函数
在上面我们实现了通讯录各功能并分模块封装好了各个函数.这时候我们回到主程序里来,进行收尾工作.
首先我们就是要实现一个菜单功能让用户选择,然后用dowhile循环和switch语句实现各个功能
具体实现如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void Menu()
{
printf("******************************\n");
printf("********1.Add***2.Del*********\n");
printf("*******3.Search***4.Show******\n");
printf("*******5.Modify***6.Sort******\n");
printf("*************0.Exit***********\n");
printf("******************************\n");
}
int main()
{
int input = 0;
struct Contact con;
InitContact(&con);
do
{
Menu();
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
break;
default:
printf("选择错误");
break;
}
} while (input);
return 0;
}
这里可以发现我的case用的是ADD,DEL什么的和菜单提示不一样啊,这是因为这样可以让读代码的人可以一目了然的读懂各个case是什么功能.那么如何实现呢,要知道case只能带数字常数或者字符常数.这时候就要用到枚举了,这个我们也可以将它放在头文件里
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
SHOW,
MODIFY,
SORT
};
就像这样,这时候如果没有赋值EXIT就会默认为0,后续跟的值会递增.就很好的达到我们想要的效果.
4.完整代码
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void Menu()
{
printf("******************************\n");
printf("********1.Add***2.Del*********\n");
printf("*******3.Search***4.Show******\n");
printf("*******5.Modify***6.Sort******\n");
printf("*************0.Exit***********\n");
printf("******************************\n");
}
int main()
{
int input = 0;
struct Contact con;
InitContact(&con);
do
{
Menu();
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SEARCH:
SearchContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case EXIT:
break;
default:
printf("选择错误");
break;
}
} while (input);
return 0;
}
Contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void InitContact(struct Contact *ps)
{
memset(ps->data, 0, sizeof(ps->data));
ps->size = 0;
}
void AddContact(struct Contact *ps)
{
if (ps->size == MAX)
{
printf("通讯录已满\n");
return;
}
else
{
printf("请输入名字>:");
scanf("%s", ps->data[ps->size].name);
printf("请输入年龄>:");
scanf("%d", &ps->data[ps->size].age);
printf("请输入性别>:");
scanf("%s", ps->data[ps->size].sex);
printf("请输入电话>:");
scanf("%s", ps->data[ps->size].tele);
printf("请输入地址>:");
scanf("%s", ps->data[ps->size].addr);
ps->size++;
printf("添加成功\n");
}
}
void ShowContact(const struct Contact* ps)
{
if (ps->size == 0)
{
printf("通讯录暂为空\n");
return;
}
else
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\t\n","名字","年龄","性别","电话","地址");
for (int i = 0; i < ps->size; i++)
{
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\t\n",
ps->data[i].name,
ps->data[i].age,
ps->data[i].sex,
ps->data[i].tele,
ps->data[i].addr
);
}
}
static int FindContactbyname(const struct Contact *ps,char name[MAX_NAME])
{
for (int i = 0; i < ps->size; i++)
{
if (0 == strcmp(name, ps->data[i].name))
{
return i;
}
}
return -1;
}
void DelContact(struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要删除人的名字>:");
scanf("%s", name);
int pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("删除的对象不存在\n");
}
else
{
for (int j = pos; j < ps->size; j++)
{
ps->data[j] = ps->data[j + 1];
}
}
printf("删除成功\n");
ps->size--;
}
void SearchContact(const struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要搜索人的名字\n");
scanf("%s", name);
int pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("查找的人不存在\n");
}
else
{
printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\t\n", "名字", "年龄", "性别", "电话", "地址");
printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\t\n",
ps->data[pos].name,
ps->data[pos].age,
ps->data[pos].sex,
ps->data[pos].tele,
ps->data[pos].addr
);
}
}
void ModifyContact(struct Contact* ps)
{
char name[MAX_NAME];
printf("请输入要修改人的名称\n");
scanf("%s", name);
int pos = FindContactbyname(ps, name);
if (pos == -1)
{
printf("要修改的人不存在\n");
}
else
{
printf("请输入名字>:\n");
scanf("%s", ps->data[pos].name);
printf("请输入年龄>:\n");
scanf("%d", &ps->data[pos].age);
printf("请输入性别>:\n");
scanf("%s", ps->data[pos].sex);
printf("请输入电话>:\n");
scanf("%s", ps->data[pos].tele);
printf("请输入地址>:\n");
scanf("%s", ps->data[pos].addr);
printf("修改成功\n");
}
}
const int* SortByName(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return strcmp(pc1->name, pc2->name);
}
const int* SortByAge(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return pc1->age - pc2->age;
}
const int* SortByTele(const void* c1, const void* c2)
{
const struct PepInfo* pc1 = (const struct PepInfo*)c1;
const struct PepInfo* pc2 = (const struct PepInfo*)c2;
return strcmp(pc1->tele, pc2->tele);
}
int (*CompareFunctions[])(const void*, const void*) =
{
SortByName,
SortByAge,
SortByTele
};
void SortContact(struct Contact* ps)
{
int choice;
printf("请选择排序规则\n");
printf("0.按名字排序\n1.按年龄排序\n2.按电话排序\n");
scanf("%d", &choice);
if (choice < 0 || choice >= 3)
{
printf("无效选择\n");
return 1;
}
qsort(ps->data,
ps->size,
sizeof(struct PepInfo),
CompareFunctions[choice]);
}
Contact.h
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 1000
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 20
enum Option
{
EXIT,
ADD,
DEL,
SEARCH,
SHOW,
MODIFY,
SORT
};
struct PepInfo
{
char name[MAX_NAME];
int age;
char sex[MAX_SEX];
char tele[MAX_TELE];
char addr[MAX_ADDR];
};
struct Contact
{
struct PepInfo data[MAX];
int size;
};
void InitContact(struct Contact* ps);
void AddContact(struct Contact* ps);
void ShowContact(const struct Contact* ps);
void DelContact(struct Contact* ps);
void SearchContact(const struct Contact *ps);
void ModifyContact(struct Contact *ps);
void SortContact(struct Contact *ps);
5.结语
很快啊,我们的通讯录就写完了,回顾一下,我们实现了增删查改排序搜索功能,但仔细一想我们现在这个程序还有很多缺陷.
1.我们现在为了存储信息一次性开辟了MAX个结构体数组,而我们又不一定能用那么多空间,就容易造成很多的内存浪费
2.通讯录功能重在保存联系人的信息,而现在仅仅只是在控制太能实现通讯录,一离开就会发现,你录入的所有信息就消失殆尽了.
而要解决这两个问题,我们就要用到动态内存管理和文件操作的知识.也就是开头说的动态版和文件版通讯录,而这些都是后话了
要想知道如何实现,就等下回分解把.哈哈哈.
自上次扫雷过后,也有很久没有分享了,博客这种事,一停下来还是有点难再动起来,大家还是快动起来把!

1385

被折叠的 条评论
为什么被折叠?



