目录
一、准备
包含两个.c文件和一个头文件,与三子棋和扫雷类似(具体看我往期博客)
1.头文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 6
#define TELENUM_MAX 12
#define ADDRESS_MAX 30
typedef struct PeoInfo
{
char Name[NAME_MAX];
char Sex[SEX_MAX];
int Age;
char TeleNum[TELENUM_MAX];
char Address[ADDRESS_MAX];
}PeoInfo;
typedef struct Contact
{
PeoInfo data[MAX];
int sz;
}Contact;
void InitContact(Contact* pc);
void AddContact(Contact* pc);
int FindByName(const Contact* pc, char Name[]);
void DelContact(Contact* pc);
void PrintContact(const Contact* pc);
void SearchByName(const Contact* pc);
void ModifyContact(Contact* pc);
void SortContact(Contact* pc);
void Menu();
int cmp_peo_by_name1(const void* p1, const void* p2);
int cmp_peo_by_name2(const void* p1, const void* p2);
1.包含库函数
2.定义全局变量,方便修改
3.创建struct PeoInfo的结构体变量,相当于通讯录的一页,元素包括姓名,年龄,性别,电话号码,地址,再转换数据类型名为PeoInfo
4.再创建Contact的结构体变量,包含结构体数组和页数,数组每个成员为PeoInfo,相当于整本通讯录
5.存放所有函数的声明
2.Contact.c
#include "contact.h"
enum Option
{
EXIT,
ADD,
SEARCH,
DELE,
MODIFY,
SORT,
PRINT
};
void menu()
{
printf("_________________CONTACT___________________\n");
printf("|********** 1.add 2.search ***********|\n");
printf("|********** 3.del 4.modify ***********|\n");
printf("|********** 5.sort 6.print ***********|\n");
printf("|********** 0.exit ***********|\n");
}
int main()
{
Contact con;
int input = 0;
InitContact(&con);
do
{
int ret = 0;
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DELE:
DelContact(&con);
break;
case SEARCH:
SearchByName(&con);
break;
case MODIFY:
ModifyContact(&con);
break;
case SORT:
SortContact(&con);
break;
case PRINT:
PrintContact(&con);
break;
case EXIT:
printf("退出通讯录\n");
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
1.创建结构体变量con
2.初始化con
3.利用do while语句循环实现通讯录多种功能重复使用,创建枚举类型使得关键字成为自然数常量,赋予其意义。
二、具体实现
1.初始化
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data, 0, sizeof(pc->data));
}
1.传过来结构体地址,接收为结构体指针
2.确保pc不为空指针
3.将页数初始化为0
4.利用memset,
2.添加新联系人
void AddContact(Contact* pc)
{
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].TeleNum);
printf("请输入地址:>");
scanf("%s", pc->data[pc->sz].Address);
pc->sz++;
printf("添加成功\n");
}
1.pc为结构体指针,通过->找到成员,当页数满了后不能添加新联系人
2.pc->data找到结构体数组,而pc->sz找到数组下标,即PeoInfo结构体,通过.找到结构体成员
3.最后pc->sz++,增加页数
4.除了age以外其他都为数组首元素地址,因此age需要&
效果示意gif
3.打印联系人
void PrintContact(const Contact* pc)
{
assert(pc);
int i = 0;
printf("%-20s %-5s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话号码", "地址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s %-5d %-5s %-12s %-30s\n", pc->data[i].Name, pc->data[i].Age, pc->data[i].Sex, pc->data[i].TeleNum, pc->data[i].Address);
}
}
1.打印时使用左对齐
2.使用for循环遍历结构体数组
效果图
4.删除联系人
void DelContact(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录已空,无法删除、\n");
return;
}
char Name[NAME_MAX] = { 0 };
printf("请输入要删除人的名字:>");
scanf("%s", Name);
int pos = FindByName(pc, Name);
if (pos == -1)
{
printf("要调整的人不存在,请重新输入\n");
return;
}
int j = 0;
for (j = pos; j < pc->sz-1; j++)
{
pc->data[j] = pc->data[j + 1];
}
pc->sz--;
printf("删除成功\n");
}
1.删除的原理为用后面一组数据覆盖前面一组
2.当页数为0,或找不到目标联系人时提前结束函数(return)
int FindByName(const Contact* pc, char Name[])
{
assert(pc);
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(Name, pc->data[i].Name))
{
return i;
}
}
return -1;
}
1.遍历整个结构体数组,利用strcmp比对输入数组与PeoInfo中的Name数组
2.找到后return当前下标,else return -1
效果图
5.调整联系人信息
void ModifyContact(Contact* pc)
{
assert(pc);
if (pc->sz == 0)
{
printf("通讯录已空,无法查找\n");
return;
}
int input = 0;
char Name[NAME_MAX] = { 0 };
printf("请输入你要调整的联系人:>\n");
scanf("%s", Name);
int pos = FindByName(pc, Name);
if (pos == -1)
{
printf("要调整的人的信息不存在,请重新输入\n");
return;
}
printf("请输入你需要调整的信息\n");
getchar();
Menu();
do
{
scanf("%d", &input);
switch (input)
{
case NAME:
printf("请输入需要修改的姓名\n");
scanf("%s", pc->data[pos].Name);
break;
case AGE:
printf("请输入需要修改的年龄\n");
scanf("%d", &pc->data[pos].Age);
break;
case SEX:
printf("请输入需要修改的性别\n");
scanf("%s", pc->data[pos].Sex);
break;
case TELENUM:
printf("请输入需要修改的电话号码\n");
scanf("%s", pc->data[pos].TeleNum);
break;
case ADDRESS:
printf("请输入需要修改的地址\n");
scanf("%s", pc->data[pos].Address);
break;
case EXIT:
printf("退出查找\n");
return;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input<=0 || input>=5);
printf("修改成功\n");
}
void Menu()
{
printf("__________________INFORMATION_____________________\n");
printf("|********** 1.Name 2.Age **************|\n");
printf("|********** 3.Sex 4.TeleNum **************|\n");
printf("|********** 5.Address 0.Exit **************|\n");
}
enum Option1
{
EXIT,
NAME,
AGE,
SEX,
TELENUM,
ADDRESS
};
1.利用之前写的FindByName找到目标的pos
2.用getchar把缓冲区的\n吃掉
3.同理创建枚举常量
4.创建菜单选择需要更改的信息
效果图
6.查找联系人
void SearchByName(const Contact* pc)
{
assert(pc);
int i = 0;
char Name[20] = { 0 };
printf("请输入需要查找的名字\n");
scanf("%s", &Name);
for (i = 0; i < pc->sz; i++)
{
if (0 == strcmp(Name, pc->data[i].Name))
{
printf("%-20s %-5s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话号码", "地址");
printf("%-20s %-5d %-5s %-12s %-30s\n", pc->data[i].Name, pc->data[i].Age, pc->data[i].Sex, pc->data[i].TeleNum, pc->data[i].Address);
return;
}
}
printf("找不到目标名字\n");
}
与FineByName同理,不过函数类型是void,直接函数内打印不返回任何值
7.排序通讯录(按名字首字母)
void SortContact(Contact* pc)
{
assert(pc);
int input = 0;
if (pc->sz == 0)
{
printf("通讯录已空,无法排序\n");
return;
}
printf("请选择排升序还是降序\n1.升序 2.降序 0.退出排序\n");
scanf("%d", &input);
do
{
switch (input)
{
case ASC:
qsort(pc->data, (size_t)pc->sz, (size_t)sizeof(PeoInfo), cmp_peo_by_name1);
break;
case DEASC:
qsort(pc->data, (size_t)pc->sz, (size_t)sizeof(PeoInfo), cmp_peo_by_name2);
break;
case EXIT:
return;
break;
default:
printf("选择错误,请重新输入\n");
break;
}
} while (input < 0 || input>2);
printf("排序成功\n");
}
1.排序要考虑升序与降序的情况
2.qsort第一个参数(Contact),data结构体数组,(PeoInfo)的首结构体地址
3.第三个为全部PeoInfo结构体的字节大小
4.比较函数中需要强制类型转换为结构体指针,然后->目标成员
5.枚举类型Option2中T为凑数成员,因为EXIT已经在Option1中定义
效果图
完