目录
一、前言
本文将会用c语言实现一个通讯录的系统,并且存储若干人的信息,每个人的信息包括:姓名,性别,年龄,电话号码,住址。此通讯录系统的功能包括: 1.增加联系人 2.删除对应的联系人 3.查找联系人 4.修改联系人的信息 5.排序此通讯录 6.打印出通讯录每个人的信息
二、通讯录的实现
1.关于通讯录的前期准备
(1)菜单的实现
首先关于一个通讯录,建立一个菜单是很重要的,菜单能够实现和用户的交互。
因此我们需要建立一个菜单,并且菜单立马应该包括通讯录立马该有的功能,以便于用户的操作
代码如下:
void menu() { printf("=====================================\n"); printf("============1.增加联系人=============\n"); printf("============2.删除联系人=============\n"); printf("============3.查找联系人=============\n"); printf("============4.修改联系人=============\n"); printf("============5.排序通讯录=============\n"); printf("============6.打印通讯录=============\n"); printf("============0.退出通讯录=============\n"); printf("=====================================\n"); }
效果差不都就是这样子
(2)关于联系人结构体的创建
这里我们需要利用结构体来实现实现前言中的通讯录功能以及联系人信息,我将利用两个结构体来构建我们需要的东西。
typedef struct PeoInfo { char name[NAME_MAX]; char sex[SEX_MAX]; int age; char tele[TELE_MAX]; char addr[ADDR_MAX]; }Peo; typedef struct Contact { Peo con[PON_MAX]; int sz;//记录数量 }contact;
关于第一个结构体Peo是关于个人信息的存储,第二个结构体构建了 我们需要的通讯录,con来作为以第一个结构体为类型的数组,sz来记录这个结构体存储个人信息的数量。
(3)实现菜单选项的功能
我们需要根据菜单里面的选项来选择进行我们需要实现的功能,比如我们想假如一个用户信息,我们就输入1就会进行用户假如的操作,我们想退出程序我们输入0就可以退出。我选择利用枚举变量的形式来实现,讲操作变成数字,利用switch选择语句来实现各自的功能。
enum Option//利用枚举变量来定义 { exit,//0 add,//1 del,//2 search,//3 modify,//4 sort,//5 print//6 }; void test() { contact con;//建立结构体 InitCon(&con); int input = 0; do { menu(); scanf("%d", &input); switch (input) { case add: AddCon(&con); break; case del: DelCon(&con); break; case search: SearchCon(&con); break; case modify: ModifyCon(&con); break; case sort: SortCon(&con); break; case print: PrintCon(&con); break; case exit: printf("退出程序,欢迎使用!\n"); break; default: printf("没有找到此数字匹配的操作!!\n"); } } while (input); }
(4)关于全局变量的定义
为了实现这些变量,并且方便后期的处理数组大小,所以我们可以利用宏来实现这个功能
#define NAME_MAX 20//姓名的长度 #define SEX_MAX 5//性别的长度 #define TELE_MAX 12//电话号码的长度 #define ADDR_MAX 30//地址长度 #define PON_MAX 1000//通讯录的大小
2、通讯录的功能实现
(1)初始化通讯录
刚刚开始我们创建了通讯录的结构体,但是我们不难发现,因为没有定义的原因,他们里面存的都是随机数,因此我们需要对他们进行初始化防止后面的失误。初始化很简单就是把我们通讯录结构体里面的用户信息的结构体类型的数组进行初始化,这里我们可以浅浅用一个memset函数来实现,当然别忘了引用string的头文件,然后sz的初始化很简单就是初始化为0。
void InitCon(contact* pc) { assert(pc); pc->sz = 0; memset(pc->con, 0, sizeof(pc->con)); }//初始化结构体
(2)增加联系人
首先第一步我们完成第一步就可以创建关于加入联系人的函数,这个很简单我们只需要访问通讯录结构体里面的数组中的每个元素然后输入对应值就可以了
void AddCon(contact* pc) { assert(pc); if (pc->sz == PON_MAX) { printf("通讯录满了!!\n"); } printf("请输入姓名:>\n"); scanf("%s", pc->con[pc->sz].name); printf("请输入性别:>\n"); scanf("%s", pc->con[pc->sz].sex); printf("请输入年龄:>\n"); scanf("%d", &(pc->con[pc->sz].age));//注意年龄在这里是一个int类型 printf("请输入电话号码:>\n"); scanf("%s", pc->con[pc->sz].tele); printf("请输入地址:>\n"); scanf("%s", pc->con[pc->sz].addr); pc->sz++; printf("此用户添加成功!\n"); }
(3)打印通讯录
打印通讯录也很简单,利用一个for循环根据sz的大小遍历结构体中的数组每个元素并且打印即可
void PrintCon(const contact* pc) { assert(pc); printf("%-15s %-5s %-5s %-12s %-30s\n","姓名","性别","年龄","电话","地址"); for (int i = 0; i < pc->sz; i++) { printf("%-15s %-5s %-5d %-12s %-30s\n", pc->con[i].name, pc->con[i].sex, pc->con[i].age, pc->con[i].tele, pc->con[i].addr); } }
(4)查找联系人
查找联系人这边我们需要构建一个函数,这个函数需要去根据我们想要寻找的姓名去在通讯录中寻找这个人所对应的位置,加入找到了就可以返回对应位置的下标,否则返回-1。找到之后就和打印通讯录的操作差不多打印出来就好了。
int FindByName(const contact* pc, char* name) { assert(pc && name); for (int i = 0; i < pc->sz; i++) { if (0 == strcmp(pc->con[i].name, name)) return i; } return -1; }//寻找或者删除联系人的下标 void SearchCon(const contact* pc) { assert(pc); char name[NAME_MAX]; printf("请输入需要寻找用户的名字:>\n"); scanf("%s", name); int pos = FindByName(pc, name);//pos为要寻找的人的下标 if (pos == -1) { printf("查无此人\n"); } else { printf("%-15s %-5s %-5s %-12s %-30s\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%-15s %-5s %-5d %-12s %-30s\n", pc->con[pos].name, pc->con[pos].sex, pc->con[pos].age, pc->con[pos].tele, pc->con[pos].addr); } }
(5)修改联系人
关于修改联系人这个内容,大多数的代码都是直接替换所有的内容,这样也会让一些本来就不用修改的信息又被修改了一遍,很麻烦,所有我的设计是,先让用户查找到需要修改的这个人然后选择是修改什么信息,然后重新输入嘞一部分的信息,这个其实就和菜单选项实现很相似,我们需要利用枚举变量,并且利用do...while语句来让他一直循环直到输入0为止,利用switch来根据用户的选项来跳到需要的操作。
enum Con { ERRO, NAME, SEX, AGE, TELE, ADDR }; void ModifyCon(contact* pc) { assert(pc); char name[NAME_MAX]; printf("请输入需要修改信息用户的名字:>\n"); scanf("%s", name); int pos = FindByName(pc, name);//pos为要寻找的人的下标 if (pos == -1) { printf("查无此人\n"); } else { int num = 0; do { printf("请输入你想修改此用户的信息\n"); printf(" 0.退出 1.姓名 2.性别 3.年龄 4.电话 5.地址:>\n"); scanf("%d", &num); switch (num) { case NAME: printf("请输入你想修改的姓名:>\n"); scanf("%s", pc->con[pos].name); break; case SEX: printf("请输入你想修改的性别:>\n"); scanf("%s", pc->con[pos].sex); break; case AGE: printf("请输入你想修改的年龄:>\n"); scanf("%d", &(pc->con[pos].age)); break; case TELE: printf("请输入你想修改的电话:>\n"); scanf("%s", pc->con[pos].tele); break; case ADDR: printf("请输入你想修改的地址:>\n"); scanf("%s", pc->con[pos].addr); break; case 0: printf("不修改退回界面\n"); break; default:printf("无效操作数!\n"); } } while (num); } }
(6)删除联系人
这个操作也不算复杂,我的思路是,首先我们先利用刚刚查找的嘞个查找下标的函数,查找到我们需要寻找删除联系人的坐标,然后对他进行删除,删除之后呢我们需要把后面的元素往前移动,这就要利用for循环,但是对于for循环的次数要多加注意,因为稍不小心就会导致数组越界。
void DelCon(contact* pc) { assert(pc); char name[NAME_MAX]; printf("请输入需要删除用户的名字:>\n"); scanf("%s", name); int pos = FindByName(pc, name);//pos为要寻找的人的下标 if (pos == -1) { printf("查无此人\n"); } else { for (int i = pos; i < pc->sz - 1; i++) { pc->con[i] = pc->con[i + 1]; } pc->sz--; printf("删除成功!\n"); } }
(7)排序通讯录
这一步我们需要按照人名首字母的大小对于通讯录进行排序,就比如summer和banni,banni会在summer,这个排序其实和冒泡排序差不多,我们需要注意的是我们在交换两个数的时候我们需要定义的的嘞个中间数为联系人结构体类型,以免出错
void SortCon(contact* pc) { if (pc->sz == 0) { printf("通讯录中没有联系人\n"); } else { for (int i = 0; i < pc->sz - 1; i++) { for (int j = 0; j < pc->sz - 1 - i; j++) { if ((strcmp(pc->con[j].name, pc->con[j + 1].name)) > 0) { Peo temp = pc->con[j]; pc->con[j] = pc->con[j + 1]; pc->con[j + 1] = temp; } } } printf("排序成功!\n"); } }
这样我们需要的一个通讯录就这样实现了!
三、关于通讯录的优化
这个通讯录我们不难发现他有一个致命的缺点,就是我们初始化的通讯录大小为1000,但是当我们存满还想存元素的时候,编译器就会给我们报错,我们就需要继续手动增加空间,所以为了避免这样我们可以利用动态内存分配来定义我们的通讯录结构体。
1、通讯录结构体的改进
这边我们可以把之前的结构体里面的数组变成一个指针数组,并且为了考虑这个通讯录来回删除增加的缘故,一个sz来记录数组元素个数是不行的,我们需要在设定一个值为数组最大的空间,当sz和他相等的时候我们就需要扩充这个数组。
typedef struct Contact { Peo* con; int sz;//记录数量 int max;//记录通讯录当前的最大容量 }contact;
2、初始化结构体
这个初始化结构体,我们需要sz初始化为0,并且为con这个指针开辟一块空间,并且赋予max一个初始值,这个初始值我们可以用宏来定义我们初始化通讯录的大小
#define CON_MAX 3//通讯录初始化大小 void InitCon(contact* pc) { assert(pc); pc->sz = 0; pc->max = CON_MAX; pc->con = (Peo*)malloc(sizeof(Peo) * pc->max); if (pc->con == NULL) { perror("InitContact::malloc"); return; } memset(pc->con, 0, pc->max * sizeof(Peo)); }
3.增容
当我们增加联系人是sz == max的时候,我们可以利用realloc函数来实现扩容,每一次扩容两个空间
void CheckCapacity(contact* pc) { //增容的代码 if (pc->sz == pc->max) { Peo* tmp = (Peo*)realloc(pc->sz, (pc->max + 2) * sizeof(Peo)); if (tmp != NULL) { pc->sz = tmp; } else { perror("CheckCapacity::realloc"); return; } pc->max += 2; printf("增容成功\n"); } }
4.释放内存
这边我们可以构建一个函数在这个程序结束之后释放内存
void DestroyContact(contact* pc) { free(pc->con); pc->con = NULL; pc->max = 0; pc->sz = 0; printf("销毁成功\n"); }
四、总结
这个通讯录主要考察的是对于结构体的访问,还有对于结构体的创建,在编码的时候得务必细心,最后放上完整的源代码供大家参考
#pragma once #include <string.h> #include <stdio.h> #include <assert.h> #include <stdlib.h> //类型的声明 #define MAX 1000 #define NAME_MAX 20 #define SEX_MAX 5 #define TELE_MAX 12 #define ADDR_MAX 30 //通讯录初始状态的容量大小 #define DEFAULT_SZ 3 enum Option { EXIT,//0 ADD, DEL, SEARCH, MODIFY, SORT, PRINT }; enum Con { ERRO, NAME, SEX, AGE, TELE, ADDR }; typedef struct PeoInfo { char name[NAME_MAX]; char sex[SEX_MAX]; int age; char tele[TELE_MAX]; char addr[ADDR_MAX]; } PeoInfo; typedef struct Contact { PeoInfo* data;//可以存放1000个人的信息 int sz;//记录通讯中已经保存的信息个数 int capacity;//记录通讯录当前的最大容量 }Contact; //函数的声明 //初始化通讯录 void InitContact(Contact* pc); //销毁通讯录 void DestroyContact(Contact* pc); //增加联系人的信息 void AddContact(Contact* pc); //打印通讯录中的信息 void PrintContact(const Contact* pc); //删除指定联系人 void DelContact(Contact* pc); //查找指定联系人 void SearchContact(const Contact* pc); //保存通讯录的信息到文件 void SaveContact(const Contact* pc); //通讯录排序 void SortCon(Contact* pc); void ModifyCon(Contact* pc); #define _CRT_SECURE_NO_WARNINGS 1 //动态的版本 //void InitContact(Contact* pc) //{ // assert(pc); // pc->sz = 0; // pc->capacity = DEFAULT_SZ; // pc->data = (PeoInfo*)malloc(pc->capacity * sizeof(PeoInfo)); // // if (pc->data == NULL) // { // perror("InitContact::malloc"); // return; // } // memset(pc->data, 0, pc->capacity * sizeof(PeoInfo)); //} void CheckCapacity(Contact* pc) { //增容的代码 if (pc->sz == pc->capacity) { PeoInfo* tmp = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo)); if (tmp != NULL) { pc->data = tmp; } else { perror("CheckCapacity::realloc"); return; } pc->capacity += 2; printf("增容成功\n"); } } void LoadContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.dat", "rb"); if (pf == NULL) { perror("LoadContact::fopen"); return; } //读文件 PeoInfo tmp = { 0 }; while (fread(&tmp, sizeof(PeoInfo), 1, pf)) { CheckCapacity(pc); pc->data[pc->sz] = tmp; pc->sz++; } //关闭文件 fclose(pf); pf = NULL; } //初始化通讯录 - 文件版本 void InitContact(Contact* pc) { assert(pc); pc->sz = 0; pc->capacity = DEFAULT_SZ; pc->data = (PeoInfo*)malloc(pc->capacity * sizeof(PeoInfo)); if (pc->data == NULL) { perror("InitContact::malloc"); return; } memset(pc->data, 0, pc->capacity * sizeof(PeoInfo)); //加载文件信息到通讯录中 LoadContact(pc); } void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; printf("销毁成功\n"); } void AddContact(Contact* pc) { assert(pc); //静态版本 //if (pc->sz == MAX) //{ // printf("通讯录已满,无法添加\n"); // return; //} //动态的版本 CheckCapacity(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].tele); printf("请输入地址:>"); scanf("%s", pc->data[pc->sz].addr); pc->sz++; printf("添加成功\n"); } 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].tele, pc->data[i].addr); } } //找到了返回下标 //找不到返回-1 int FindByName(const Contact* pc, char name[]) { assert(pc); int i = 0; for (i = 0; i < pc->sz; i++) { if (0 == strcmp(pc->data[i].name, name)) { return i; } } return -1; } void DelContact(Contact* pc) { assert(pc); if (pc->sz == 0) { printf("通讯录已空,无法删除\n"); return; } //删除 //1. 找到 char name[NAME_MAX] = { 0 }; printf("请输入要删除人的名字:>"); scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) { printf("要删除的人不存在\n"); return; } //2. 删除 int j = 0; for (j = pos; j < pc->sz - 1; j++) { pc->data[j] = pc->data[j + 1]; } pc->sz--; printf("删除成功\n"); } void SearchContact(const Contact* pc) { char name[NAME_MAX] = { 0 }; printf("请输入要查找人的名字:>"); scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) { printf("要查找的人不存在\n"); return; } printf("%-20s %-5s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-20s %-5d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } void SaveContact(const Contact* pc) { FILE* pf = fopen("contact.dat", "wb"); //回来本地建立一个contact.dat的记事本 if (pf == NULL) { perror("SaveContact::fopen"); return; } //写文件 int i = 0; for (i = 0; i < pc->sz; i++) { fwrite(pc->data + i, sizeof(PeoInfo), 1, pf); } //关闭文件 fclose(pf); pf = NULL; } void SortCon(Contact* pc) { if (pc->sz == 0) { printf("通讯录中没有联系人\n"); } else { for (int i = 0; i < pc->sz - 1; i++) { for (int j = 0; j < pc->sz - 1 - i; j++) { if ((strcmp(pc->data[j].name, pc->data[j + 1].name)) > 0) { PeoInfo temp = pc->data[j]; pc->data[j] = pc->data[j + 1]; pc->data[j + 1] = temp; } } } printf("排序成功!\n"); } } void ModifyCon(Contact* pc) { assert(pc); char name[NAME_MAX]; printf("请输入需要修改信息用户的名字:>\n"); scanf("%s", name); int pos = FindByName(pc, name);//pos为要寻找的人的下标 if (pos == -1) { printf("查无此人\n"); } else { int num = 0; do { printf("请输入你想修改此用户的信息\n"); printf(" 0.退出 1.姓名 2.性别 3.年龄 4.电话 5.地址:>\n"); scanf("%d", &num); switch (num) { case NAME: printf("请输入你想修改的姓名:>\n"); scanf("%s", pc->data[pos].name); break; case SEX: printf("请输入你想修改的性别:>\n"); scanf("%s", pc->data[pos].sex); break; case AGE: printf("请输入你想修改的年龄:>\n"); scanf("%d", &(pc->data[pos].age)); break; case TELE: printf("请输入你想修改的电话:>\n"); scanf("%s", pc->data[pos].tele); break; case ADDR: printf("请输入你想修改的地址:>\n"); scanf("%s", pc->data[pos].addr); break; case 0: printf("不修改退回界面\n"); break; default:printf("无效操作数!\n"); } } while (num); } } void menu() { printf("*****************************************\n"); printf("**** 1.增加联系人 2.删除联系人 ****\n"); printf("**** 3.查找联系人 4.修改联系人 ****\n"); printf("**** 5.通讯录排序 6.打印通讯录 ****\n"); printf("**** 0.退出 ****\n"); printf("******************************************\n"); } void test() { int input = 0; Contact con; InitContact(&con); do { menu(); printf("请输入你的选择:>"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DEL: DelContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyCon(&con); break; case SORT: SortCon(&con); break; case PRINT: PrintContact(&con); break; case EXIT: SaveContact(&con); DestroyContact(&con); printf("感谢使用\n"); break; default: printf("非法输入\n"); break; } } while (input); } int main() { test(); return 0; }
感谢阅读!!!!