目录
前言:
大家好,今天写了一个通讯录系统,本程序也是对结构体的考察,我会尽可能阐述出自己的思路以及重要的知识点,大家也可以看看注释!
设计思路:
1.添加联系人
2.删除联系人
3.修改联系人信息
4.查找指定联系人
5.显示通讯录
6.排序通讯录
7.清除所有联系人
这是我封装的一些函数:
// 显示菜单
void Show_menu(void);
// 初始化通讯录
void Init_Contacts(contacts *pc);
// 添加联系人
void Add_Contacts(contacts *pc);
// 显示通讯录
void Show_Contacts(contacts *pc);
// 修改联系人信息
void Modifly_Contacts(contacts *pc);
// 查找指定联系人
int Find_Contacts(contacts *pc, char *name_t, int pos[MAX]);
// 删除联系人
void Del_Contacts(contacts *pc);
// 查找联系人
void Seek_Contacts(contacts *pc);
// 排序通讯录
void Sort_Contacts(contacts *pc);
// 清空联系人
void Clear_Contacts(contacts *pc);
//判断联系人是否重复
int Determine_same(contacts *pc);
基本功能:
游戏主体:
int main()
{
int choose = 0;
contacts con;
Init_Contacts(&con); //初始化通讯录
do
{
Show_menu(); //显示菜单
printf("Your choose is -->");
scanf("%d",&choose);
switch(choose)
{
case 1: //添加联系人
Add_Contacts(&con); break;
case 2: //删除联系人
Del_Contacts(&con); break;
case 3: //修改联系人
Modifly_Contacts(&con); break;
case 4: //查找联系人
Seek_Contacts(&con); break;
case 5: //显示通讯录
Show_Contacts(&con); break;
case 6: //排序通讯录
Sort_Contacts(&con); break;
case 7://清空所有联系人
Clear_Contacts(&con); break;
case 0:
printf("Exit Successful!\n"); break;
default: //输入错误,请重新输入
printf("Input error!\n"); break;
}
} while (choose);
system("pause");
return 0;
}
首先,初始化了结构体,然后不断的在循环里选择自己所需的操作,在使用函数时,我们应当将结构体的地址作为参数传入,以便更好的修改
显示菜单:
// 显示菜单
void Show_menu()
{
printf("**************************************\n");
printf("***** 1.add 2.del *****\n");
printf("***** 3.modifly 4.seek *****\n");
printf("***** 5.show 6.sort *****\n");
printf("***** 7.Clear 0.exit *****\n");
printf("**************************************\n");
}
定义结构体:
//最大字节空间
#define MAX_NAME 9
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDER 30
// 类型声明
typedef struct PeoInfo
{
char name[MAX_NAME]; // 名字
int age; // 年龄
char sex[MAX_SEX]; // 性别
char tele[MAX_TELE]; // 电话
char addr[MAX_ADDER]; // 地址
} PeoInfo;
这是在.h文件中创建的一个完整的通讯录结构体,这里使用define定义的常量,方便以后的修改。
初始化结构体:
#define MAX 100 //最多人数
typedef struct contacts
{
PeoInfo data[MAX]; // 存储信息
int count; // 记录人数
} contacts;
count表示当前存储的人数,他会随着增加或减少,PeoInfo date里面存放着通讯录的一些信息,这样写的好处在于方便管理和引用
// 初始化通讯录
void Init_Contacts(contacts *pc)
{
assert(pc);
pc->count = 0;
memset(pc->data, 0, sizeof(pc->data)); // 全部初始化
}
在添加联系人信息之前,需要把data和sz全都初始化为0
assert是防止pc为NULL(空指针)它的头文件是<assert.h>,我们在使用指针时应该判断一下,这是一个很好的习惯
memset是字符串函数它的头文件是<string.h>,这里的作用是把data [MAX] 里面的空间全部设置为0
这里你也可以用循坏来初始化,都可以
这两个东西你们也可以自行查询一下,以下不在过多赘述
添加联系人:
// 添加联系人
void Add_Contacts(contacts *pc)
{
assert(pc);
if (pc->count == MAX) // 当联系人已满时,打印信息直接结束
{
printf("No Vacancies!\n");
printf("Please afrsh choose!\n");
return;
}
else
{
// 输入信息
printf("Please input name->");
scanf("%s", pc->data[pc->count].name);
printf("Please input age->");
scanf("%d", &(pc->data[pc->count].age));
printf("Please input sex->");
scanf("%s", pc->data[pc->count].sex);
printf("Please input telephone->");
scanf("%s", pc->data[pc->count].tele);
printf("Please input address->");
scanf("%s", pc->data[pc->count].addr);
}
// 判断是否重复
if (Determine_same(pc) == 1) // 判断是否有重复,无重复返回1 有重复返回0
{
pc->count++; // 输入成功,人数+1
printf("added successfully!\n");
}
else
printf("This contact already exists!!!\n"); // 已有这个联系人
}
我们在添加之前,应该先判断人数是否已满,没有满我们再让其输入信息
count是作为接收人的个数,所以直接可以拿来当下标,当全部输入完信息后,再对人数count + 1
这里我们是不是还应该再判断一下,输入的信息与以往输入的信息是否重复?如果重复那么应该就是无效输入了吧!
所以我们用一个Determine_same函数来判断一下
// 判断是否有完全相同信息的联系人
int Determine_same(contacts *pc)
{
contacts *pc1 = pc; // 创建pc1,将pc的起始位置给pc1,使其共享一块内存
for (int i = 0; i < pc->count; i++) // 遍历整个数组看,看刚刚输入与之前的没有没有完全重复的
{ // 这里判断条件是i小于pc->count所以永远也不会出现和它自身比较的情况
if (strcmp(pc->data[i].name, pc1->data[pc1->count].name) == 0)
if (pc->data[i].age == pc1->data[pc1->count].age)
if (strcmp(pc->data[i].sex, pc1->data[pc1->count].sex) == 0)
if (strcmp(pc->data[i].tele, pc1->data[pc1->count].tele) == 0)
if (strcmp(pc->data[i].addr, pc1->data[pc1->count].addr) == 0)
// 如果有---->则不再人数加1,下一回还是从这个数值输入
return 0;
}
// 如果没有,则返回1,人数count正常+1
return 1;
}
显示通讯录:
// 显示通讯录
void Show_Contacts(contacts *pc)
{
assert(pc);
if (pc->count == 0) // 如果通讯录为空,则直接返回
{
printf("This contacts is empty!\n");
return;
}
int i = 0;
//列标题
printf("%-15s\t%-15s\t%-15s\t%-15s\t%-15s\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->count; i++)
{
printf("%-15s\t%-15d\t%-15s\t%-15s\t%-15s\n",
pc->data[i].name, // 名字
pc->data[i].age, // 年龄
pc->data[i].sex, // 性别
pc->data[i].tele, // 电话
pc->data[i].addr); // 地址
}
}
这里我们打印了列标题以及各个信息为了美观我们可以将它们占位符统一一下,这里的 '-'表示左对齐,如果没有就是右对齐
利用循环遍历,将所有已经录入的信息打印出来
修改联系人信息:
// 修改联系人信息
void Modifly_Contacts(contacts *pc)
{
assert(pc);
char name_t[10] = {'\0'};
printf("Please input your want modifly name->");
scanf("%s", name_t); // 提供所修改信息的名称
// gets(name_t); *需要清除缓冲区
int pos[MAX] = {0}; // 存储的所要修改数据的下标
int same_count = Find_Contacts(pc, name_t, pos); // 计算与输入的名称相同的个数(考虑重复情况)
int select = 1; // 当出现重复数据时为用户提供选择,select=1防止没有重复时,无法正确修改信息
if (same_count == 0) // 输入要修改名称没有时,意味输入错误程序结束
{
printf("This contacts is empty,please afrsh choose!\n");
return;
}
else if (same_count > 1) // 当出现多个相同名称时,再进一步提供选择第几个
{
printf("Detected multiple players before making further choices->");
scanf("%s", &select);
while (select > same_count) // 在循环中,所做选择序号不能超过出现的个数
{
printf("Input error!!!\n");
printf("Please afrsh input->");
scanf("%d", &select);
}
}
// 修改数据-->pos数组里存储的是所出现数的下标
printf("Please input name->");
scanf("%s", pc->data[pos[select - 1]].name);
printf("Please input age->");
scanf("%d", &(pc->data[pos[select - 1]].age));
printf("Please input sex->");
scanf("%s", pc->data[pos[select - 1]].sex);
printf("Please input telephone->");
scanf("%s", pc->data[pos[select - 1]].tele);
printf("Please input address->");
scanf("%s", pc->data[pos[select - 1]].addr);
}
首先判断源数据里是否有所输入的数据,如果没有则返回,否则继续
定义一个数组name_t,用来接收用户所要修改的名称
(注意,这里如果用gets()函数来接收的话,需要清楚一个缓冲区,不然上一回的’\n‘就会直接当成这一回的输入内容)
Find_Conatsts()函数就是判断所输入的数据,源数据里有多少,然后用pos数组来存放各个相同名称的下标,后面我们会详细展开Find_Conatcts()函数
当有多个相同名称的数据时,我们将它打印出来,用户做选择,是比实际下标多1的,所以下面我们在输入时用的是select - 1这样就能定位到所指派的pos下标,此时pos数组存放的刚好是相同数据在结构体里的下标,这样就能完成正确赋值
当有多个相同名称数据时,用户做选择,然后再一一赋值,没有多个相同名称就给select初始化为1,这样也能正确访问到该位置的数组
查找联系人:
// 排查联系人
int Find_Contacts(contacts *pc, char *name_t, int pos[MAX])
{
assert(pc);
int i = 0;
int tag = 0;
printf("Find the follwing content for you :\n");
for (i = 0; i < pc->count; i++)
{
// 如果所要修改数据与源数据相同时,输出出来并将下标存放到pos数组
if (strcmp(pc->data[i].name, name_t) == 0)
{
printf("%-15s\t%-15d\t%-15s\t%-15s\t%-15s\n",
pc->data[i].name,
pc->data[i].age,
pc->data[i].sex,
pc->data[i].addr);
pc->data[i].tele,
pos[tag++] = i; // 存储下标位置
}
}
return tag; // 返回个数
}
因为我们考虑了多条相同数据的情况,所以这里的Find_Contacts()函数可能复杂些
这里的功能是:所传入的输入name_t遍历整个源数组,当有相同时,我们将其输出出来(以供用户进一步选择)
然后将下标存放到pos数组中(为了我们后期能够准确找到所选择数据在源结构体中的位置)
然后将其个数返回(为了判断是否要供用户选择这一条件),我们在修改联系人信息和删除联系人信息都提供了极大的帮助
删除联系人:
// 删除联系人
void Del_Contacts(contacts *pc)
{
assert(pc);
char name_t[10] = {'\0'};
printf("Please input your want modifly name->");
scanf("%s", name_t); // 提供所修改信息的名称
int pos[MAX] = {0};
int same_count = Find_Contacts(pc, name_t, pos);
int select = 1;
if (same_count == 0) // 当源数据中没有要删除的数据时
{
printf("This contacts is empty,please afrsh choose!\n");
return;
}
else if (same_count > 1) // 当出现多个相同名称时,再进一步提供选择第几个
{
printf("Detected multiple players before making further choices->");
scanf("%d", &select);
while (select > same_count) // 再循环中,所做选择序号不能超过出现的个数
{
printf("Input error!!!\n");
printf("Please afrsh input->");
scanf("%s", &select);
}
}
// 删除数据
int i = 0;
for (i = pos[select - 1]; i < pc->count - 1; i++) // count - 1 避免越界
pc->data[i] = pc->data[i + 1];
printf("Delete Successful!\n"); // 打印成功信息,人数 - 1
pc->count--;
}
删除数据与前面的修改数据大致是一模一样的,都是简单的对它进行一下判断,唯一不同的是最后一点->如何删除
我们可以用所要删除的数据为其实下标,让后面的数据不断覆盖前面的数据,这样就实现了删除功能,最后再将人数 - 1
但是中间的判断条件为pc->count - 1 避免出现越界访问情况
排序通讯录:
// 排序通讯录
void Sort_Contacts(contacts *pc)
{
assert(pc);
if (pc->count == 0) // 通讯录为空,直接结束
{
printf("This contacts is empty,please afrsh choose!\n");
return;
}
// 不为空,则使用qsort排序
else
qsort(pc, pc->count, sizeof(PeoInfo), cmp_peo_name);
printf("Sorting Successful!\n"); // 排序成功
}
// qsort排序所用函数(升序)
int cmp_peo_name(const void *e1, const void *e2)
{
return strcmp(((PeoInfo *)e1)->name, ((PeoInfo *)e2)->name);
}
先进行判断是否为空,为空则打印提示信息结束,不为空则用qsort()函数进行排序,它的头文件是<stdilb.h>有兴趣的可以详细学习一个这个函数,当然你也可以用两重for循环那种方法,定义一个结构体作为中间变量来交换,方法不唯一
本程序使用的升序,如果想用降序则将第二块代码中的两个参数调换一个位置
清空联系人:
// 清空联系人
void Clear_Contacts(contacts *pc)
{
if(pc->count == 0) //如果通讯录本身为空则打印提示信息返回
{
printf("This contacts is empty,please afrsh choose!\n");
return;
}
printf("Are you sure you want to clear all ?\n");
printf("Yes->1 exit->other\n");
printf("Your choose is->");
int flag = 1;
scanf("%d",&flag);
if(flag == 1)
{
Init_Contacts(pc);
printf("Clear successful!\n");
}
}
很简单,就是再次调用一下初始化函数!
GitHub - HackerActivists/Contacts: Static_Contacts_C
这是我的一个源代码,大家可以去看看!