- 用C语言实现个人通讯录,完成个人通讯录的增加、减少、查找、修改联系人,同时实现通讯录的排序功能和隐藏空间的功能等;
- 分三个文件实现:Test.c 这个文件用于测试代码,测试功能实现,以及最后的主函数,都可以用这个实现。(注意:在写项目过程中,一定要每实现一个功能就要运行测试,不然,代码过长调试困难)Contact.h文件,用于声明各种定义声明以及内部库的引用,这样可以简化代码,让代码更加可读(类型定义、库函数声明、函数定义、宏定义等)Contact.c这就是最关键的文件,用于对各项函数的实现;
1.基本思路介绍:
1.1基本思路:
- 我们的基本思路其实在Test.c文件中最能体现:
#include"Contact.h" int main() { int input; //菜单 mune1(); //若输入的是菜单选择里面的,就执行但不跳出循环,处于等待界面 //若输入为0,则判断为FALSE,跳出循环,退出通讯录 //创建通讯录: Contact con; //初始化函数:传入指针减少内存占用 InitContact(&con); do { //选项的输入 printf("请输入选项>:"); scanf("%d", &input); //若输入的是菜单选择里面的,就执行但不跳出循环,处于等待界面 //若输入为0,则判断为FALSE,跳出循环,退出通讯录 switch (input) { case ADD: AddContact(&con); break; case DEL: DelContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case SHOW: PrintContact(&con); break; case SORT: SortContact(&con); break; case EXIT: //保存到文件中 SaveContact(&con); //销毁通讯录空间 DestroyContact(&con); printf("退出通讯录\n"); break; case Hide_Room: //将当前写入文件保存到文件中 SaveContact(&con); //销毁当前空间 DestroyContact(&con); if (EncryptionContact()) { //初始化原本空间,进入加密空间 Hide_InitContact(&con); ShowHide(); //加密程序空间功能 func(&con); goto judge; } else { goto judge; } default: printf("输入错误,请重新输入\n"); break; } } while (input); judge: return 0; }
这里的基本思路就是:
- 通讯录的建立:先定义通讯录,在初始化通讯录,也就是为通讯录内部创建空间,以便存放数据;
- 功能的选择实现:创建好通讯录接下来我们肯定就是调用功能,我们用循环来实现功能的反复调用,用Switch语句来实现功能的选择(调用)。这里的基本功能是七项(方便展示可以定义一个菜单)
这就是整体基本思路,在Test.c中一目了然。
2.通讯录的具体实现:
2.1 通讯录的建立:
1.我们描述一个人是复杂的,不是一个类型元素可以描述,比如姓名是字符串,年龄是整形,所以这里要采用自定义类型(结构体)来定义联系人信息:
//人的信息 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char address[ADDR_MAX]; char phone[PHONE_MAX]; char class[CLASS_MAX]; }PeoInfo;
这里我们定义了联系人的姓名、年龄、性别、地址、电话、以及分组;
2.个人通讯录相当于一个数组,元素是PeoInfo类型(联系人),这里我们可以采用静态数组,但实际上静态数组在后续的添加人数的时候,扩容比较呆板,所以这里我们选择的是顺序表的数据结构,这更方便于增添人数(当然链表也可以实现,后续补充)
//以顺序表实现 typedef struct Contact { //创建通讯录 PeoInfo* data; int sz;//记录当前通讯录中的人数 int capacity;//记录当前通讯录的空间大小 }Contact;
通过这个代码我们就可以定义一个通讯录喽!当然接下来就要初始化通讯录,创建空间:
void InitContact(Contact* pc) { assert(pc); pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * INIT_MAX); //在堆上申请空间 if (pc->data == NULL) { //检验是否申请成功,方便调试检验; perror("malloc error"); } pc->sz = 0;//最初空间人数 pc->capacity = INIT_MAX;//空间大小 }
这里的代码表示我们在堆上申请空间,申请了INIT_MAX个人数空间,OK这里通讯录的建立就完成咯!
2.2通讯录功能:
通讯录具体有什么功能需要实现呢?这里只需要看一眼Test.h就一目了然了:
#pragma once //类型的定义和声明放入头文件 #include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #include<stdbool.h> #include<windows.h> //宏定义: #define INIT_MAX 4 #define NAME_MAX 20 #define SEX_MAX 5 #define ADDR_MAX 20 #define PHONE_MAX 12 #define CLASS_MAX 20 //选择菜单 void mune1(); void mune(); //枚举优化选项 enum input { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, Hide_Room = 110, }; enum way { S_ByName = 1, S_ByAge, S_BySex, S_ByAddr, S_ByPhone, S_ByClass, }; //人的信息 typedef struct PeoInfo { char name[NAME_MAX]; int age; char sex[SEX_MAX]; char address[ADDR_MAX]; char phone[PHONE_MAX]; char class[CLASS_MAX]; }PeoInfo; //以顺序表实现 typedef struct Contact { //创建通讯录 PeoInfo* data; int sz; int capacity; }Contact; //初始化结构体 void InitContact(Contact* pc); //扩容函数 void CapacityContact(Contact* pc); //增加联系人 void AddContact(Contact* pc); //删除联系人 void DelContact(Contact* pc); //查找联系人 int SearchContact(const Contact* pc); //查找方法: // 按名字查找: int SearchByName(const Contact* ps); // 按年龄查找: void SearchByAge(const Contact* ps); // 按性别查找: void SearchBySex(const Contact* ps); // 按地址查找: void SearchByAddr(const Contact* ps); //按分组查找: void SearchByClass(const Contact* ps); // 按电话查找: int SearchByPhone(const Contact* ps); //修改联系人 void ModifyContact(Contact* pc); //打印通讯录 void PrintContact(const Contact* pc); //对通讯录成员进行排序 void SortContact(const Contact* pc); //销毁通讯录内存空间 void DestroyContact(Contact* pc); //保存通讯录到磁盘 void SaveContact(const Contact* pc); //加载通讯录信息 void LondContact(Contact* pc); //通讯录按照名字排序 void SortContact(const Contact* pc); //通讯录成员信息交换 void Swap(PeoInfo* left, PeoInfo* right); //双指针快排 void DP_QuickSort(PeoInfo* a, int left, int right); //选择排序 void InsertSort(PeoInfo* a, int n); //隐蔽空间定义 void Hide_InitContact(Contact* pc); //隐蔽空间文件调用 void Hide_LondContact(Contact* pc); //隐蔽空间密码登录 bool EncryptionContact(); //进入展示 void ShowHide(); //隐蔽空间文件保存 void Hide_SaveContact(const Contact* pc); void func(Contact* pc);
可以看到.h文件里包含了很多内容:头文件定义,宏定义,自定义类型声明,函数声明等等;
接下来我们就具体实现一下上述功能函数:
3.具体功能函数的实现:
这里就是最重要的地方咯,注意咯!!!
3.1 增添联系人:
增添联系人代码实现:
void AddContact(Contact* pc) { assert(pc); //自动扩容 CapacityContact(pc); 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].address); printf("请输入联系人电话:"); scanf("%s", pc->data[pc->sz].phone); printf("请设置联系人组别:"); scanf("%s", pc->data[pc->sz].class); //这里sz记录当前通讯录人数 pc->sz++; return; }
运行效果:
增添联系人代码比较简单,但要注意一个问题,我们不断添加人数,会达到通讯录初始化的最大空间容量,所以我们需要考虑通讯录的扩容问题,如果我们按照最开始的想法用数组来存储联系人,则不能灵活的更改通讯录容量,需要手动更改代码空间比较麻烦,而用顺序表就可以很好解决这个问题;
void CapacityContact(Contact* pc) { if (pc->sz == pc->capacity) { pc->data = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo) * INIT_MAX * 2); if (pc->data == NULL) { perror("realloc error"); } pc->capacity = pc->capacity * 2; printf("扩容成功\n"); } }
当sz(当前通讯录人数)等于capacity(通讯录空间大小),则可以通过动态内存管理realloc,对原来的空间扩容,可以自动实现就十分方便;
3.2 删除联系人:
1.删除联系人需要考虑,通讯录中是否储存了联系人,判断通讯录是否为空;
2.实际情况中,我们删除是根据人的姓名等信息,所以这里就要具体得到要删除对象的位置,这里就要用查找功能;
3.删除的思想:覆盖+下标索引减小,实现部分空间无法访问,间接的删除;
void DelContact(Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录中无对象可操作!请先增加!"); mune1(); return; } int ret = SearchByName(pc); if (ret == -1) { printf("删除失败请重新操作!"); mune1(); return; } int i = ret; for (i = ret; i < pc->sz - 1; i++) { pc->data[i] = pc->data[i + 1]; } printf("删除成功!\n"); //这里并不是完全删除,把\0发给int时默认为零,我们sz减减,就相当于访问不到那片空间 pc->sz--; mune1(); }
运行效果:
3.3 查找联系人:
1.这里就用最简单的方法:遍历通讯录对比实现
2.当然这里我提供了六种查找方式,当然最也可以算是五种,因为最后一种分类查找我把它定为分组;
总查找函数:
int SearchContact(const Contact* pc) { assert(pc); //多种方式查找: mune(); int input; int ret = -1; do { //选项的输入 printf("请输入选项>:"); scanf("%d", &input); //若输入的是菜单选择里面的,就执行但不跳出循环,处于等待界面 //若输入为0,则判断为FALSE,跳出循环, switch (input) { case S_ByName: ret = SearchByName(pc); break; case S_ByAge: SearchByAge(pc); break; case S_BySex: SearchBySex(pc); break; case S_ByAddr: SearchByAddr(pc); break; case S_ByPhone: ret = SearchByPhone(pc); break; case S_ByClass: SearchByClass(pc); break; case EXIT: printf("查找完成,已经返回上一级\n"); mune1(); break; default: printf("该查找方式还待开发哦,请重新输入\n"); break; } } while (input); return ret; }
这里和test.c中相似,各类查找也差不多相同,这里我就列出一个比较常用的咯!
- 按照姓名查找:
int SearchByName(const Contact* ps) { int i = 0; char name[NAME_MAX]; int k = 1; printf("请输入需要的联系人名字:"); scanf("%s", name); for (i = 0; i < ps->sz; i++) { if (strcmp(name, ps->data[i].name) == 0) { //暂时不考虑重名 if (k == 1) { printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\t%-12s\n", "姓名", "年龄", "性别", "地址", "电话", "组别"); k--; } printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\t%-12s\n", ps->data[i].name, ps->data[i].age, ps->data[i].sex, ps->data[i].address, ps->data[i].phone, ps->data[i].class); return i; } } printf("无法找到该联系人信息\n"); return -1; }
运行效果:
(这里存在缺陷:就是重名,因为这里函数要返回int,所以只能用于不重名查找,当然如果重名,我们多掉用两次就可以了,还有就是通讯录里重名应该有其他备注吧哈哈!)
3.4 修改联系人:
这个代码也比较简单思路是:找到,并且输入新信息覆盖即可;
void ModifyContact(Contact* pc) { int ret = -1; int i; i = SearchByName(pc); if (i != ret) { printf("请输入修改的内容(部分):\n"); printf("请输入联系人名字:"); scanf("%s", pc->data[i].name); printf("请输入联系人年龄:"); scanf("%d", &(pc->data[i].age)); printf("请输入联系人性别:"); scanf("%s", pc->data[i].sex); printf("请输入联系人地址:"); scanf("%s", pc->data[i].address); printf("请输入联系人电话:"); scanf("%s", pc->data[i].phone); } }
运行效果:
3.5 打印通讯录:
这个功能应该最先完成,因为有了它方便调试每个功能的代码;
思路很简单:遍历打印(但要注意格式美化,不然可太挫了)
void PrintContact(const Contact* pc) { assert(pc); int i = 0; printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\t%-12s\n", "姓名", "年龄", "性别", "地址", "电话", "组别"); for (i = 0; i < pc->sz; i++) { printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\t%-12s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].address, pc->data[i].phone, pc->data[i].class); } }
C语言实现个人通讯录(功能优化)-2