c语言实现通讯录 (源码+解析+思路) 您所在的位置:网站首页 通讯录管理设计报告 c语言实现通讯录 (源码+解析+思路)

c语言实现通讯录 (源码+解析+思路)

#c语言实现通讯录 (源码+解析+思路)| 来源: 网络整理| 查看: 265

文章目录 前言一、主要的实现思路二、具体功能的函数模块1.创建结构体的类型及动态内存的开辟,信息的载入和保存2.增加联系人3.打印通讯录4.删除联系人5.查找联系人6.更改联系人信息7.给通讯录排序8.销毁通讯录 三、源码四,总结

前言

在c语言学习中记录自己的学习过程,希望对你有所帮助,这是一篇关于用c实现通讯录的细节解释及思路展开的一篇文章

一、主要的实现思路

1,首先通讯录是用来存放联系人的信息,一个人信息的种类多样,所以这里使用结构体来存放联系人的信息。 2,一个通讯录所包含的联系人肯定不止一个,所以需要一个数组来存放,因为创建了联系人结构体的类型,所以需要使用相同结构体类型的数组来存放多个人的信息。(这里采用了动态内存的方式来存储联系人,传回来的为动态内存的首地址,也可以将其看为一个结构体类型的数组来使用) 3,通讯录的主要功能:增删查改联系人,打印通讯录,排序。 增加联系人:往数组里面放元素。 删除联系人:找到联系人,将需要删除的联系人后面的联系人前移一位实现覆盖删除。 查找联系人:一般按名字查找,字符串比较。 修改联系人:找到联系人的位置,在相同位置从新录入信息实现覆盖修改。 打印通讯录:就是打印数组,数组元素为结构体。 联系人排序:选择用名字排序还是年龄排序或者其他信息排序,可以使用qsort函数来实现。 4,使用动态内存开辟的方法来储存联系人,节约内存。 5,使用文件操作来保存信息和写入信息,使系统运行时自动载入已保存的信息。

二、具体功能的函数模块 1.创建结构体的类型及动态内存的开辟,信息的载入和保存 typedef struct peoinfo //联系人信息 { char name[MAX_NAME]; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; int age; }peoinfo; typedef struct Contact //动态内存通讯录 { peoinfo* data;//动态内存的首地址 int sz;//联系人个数 int capacity;//最大容量 }Contact;

如上代码所示,定义两个结构体,一个用的保存联系人的信息,另一个为保存通讯录。

void LoadContact(Contact* pc)//加载通讯录 { FILE* pf = fopen("Contact.txt", "r");//以读的方式打开一个文件 if (pf == NULL) { perror("LoadContact"); return; } peoinfo temp = { 0 }; while (fread(&temp, sizeof(peoinfo), 1, pf)) { Check_Capacity(pc); pc->data[pc->sz] = temp; pc->sz++; } /*peoinfo* pa = pc->data; fread(pa,sizeof(peoinfo), 100, pf);*/ fclose(pf);//关闭文件 pf = NULL; } void InitContact(Contact* pc)//初始化动态通讯录 { pc->data = (peoinfo*)calloc(MAX_PEONUM, sizeof(peoinfo));//在堆区分配一块有MAX_PEONUM*sizeof(peoinfo)的一块内存,并且每个字节初始化为0 pc->sz = 0; pc->capacity = MAX_PEONUM; LoadContact(pc);//加载通讯录 }

初始化函数InitContact,初始化通讯录,使用calloc(动态内存开辟函数)开辟动态内存来存放联系人的信息,sz为有效联系人个数,capacity为通讯录的最大容量。开辟好空间之后LoadContact来加载之前保留的信息,以写的方式读取文件,若是首次运行则找不到这个文件,打开失败: 第一行会报错,添加好信息之后选择退出后会执行保存通讯录这个函数,以写的方式打开一个文件夹来保存信息,再次运行之后就没有这个错误,保存文件的函数如下:

void SaveContact(Contact* pc)//保存通讯录 { FILE* pf = fopen("Contact.txt", "w");//以写的方式打开一个文件 if (pf == NULL) { perror("SaveContact"); return; } //int i = 0; //for (i = 0; i < pc->sz; i++) //{ // fwrite(pc->data+i, sizeof(peoinfo), 1 , pf);//将联系人的信息写入一个文件 //} fwrite(pc->data, sizeof(peoinfo), pc->sz, pf); fclose(pf);//关闭文件 pf = NULL; } 2.增加联系人 void Addpeoinfo(Contact* pc)//增加联系人(动态内存) { Check_Capacity(pc); printf("请输入名字:>"); scanf("%s", pc->data[pc->sz].name); printf("请输入性别:>"); scanf("%s", pc->data[pc->sz].sex); printf("请输入年龄:>"); scanf("%d", &pc->data[pc->sz].age); printf("请输入号码:>"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址:>"); scanf("%s", pc->data[pc->sz].addr); pc->sz++; printf("添加成功\n"); }

添加联系人就是往开辟好的内存里面存放联系人的结构体信息,在每次添加之前需要先判断通讯录是否已经满了,若是满了需要增容,使用realloc来重新分配动态内存,这里每次满了之后默认增加两个联系人的空间,成功增加联系人之后总数加1,sz+1。检查容量函数如下:

void Check_Capacity(Contact* pc) { if (pc->sz == pc->capacity) { peoinfo* ptr = (peoinfo*)realloc(pc->data, (pc->capacity + MAX_SZ) * sizeof(peoinfo)); if (ptr != NULL) { pc->data = ptr; pc->capacity += MAX_SZ; printf("增容成功\n"); } else { perror("Addpeoinfo"); printf("通讯录已满,增容失败无法添加\n"); return; } } } 3.打印通讯录

就是将有效的联系人全部打印出来,本质上为打印结构体,先找到首地址,然后依次打印内存中存放的有效联系人,实现函数如下:

void PrintContact(const Contact* pc)//打印通讯录 { printf("------------------------通讯录--------------------------\n"); printf("%-10s %-5s %-5s %-20s %-20s\n", "名字", "性别", "年龄", "电话", "住址"); int i = 0; for (i = 0; i sz; i++) { printf("%-10s %-5s %-5d %-20s %-20s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tele, pc->data[i].addr); printf("\n"); } printf("--------------------------------------------------------\n"); }

打印的样式根据个人设计。

4.删除联系人

删除联系人就是找到需要删除的联系人,然后将后面的联系人都往前移动一位,这里最后一位联系人可能会被后面空白覆盖,所以判断循环的条件改为 idata[i] = pc->data[i + 1];此时i+1最大值位sz-1就是最后一个联系人的位置,当sz-1覆盖到sz-2时,原位置上的数据还是存在的,但是已经不是有效的联系人了,打印时不打印就可以,移动完成之后,总数减1即sz-1。 而删除之前需要找到联系人的位置,这里通过名字查找代码如下:

static int Find(const Contact* pc, const char* name)//查找联系人,找到返回坐标,找不到返回-1 { assert(name && pc); int i = 0; for (i = 0; i sz; i++) { if (strcmp(name, pc->data[i].name) == 0) return i;//找到返回下标 } return -1; }

查找函数通过strcmp比较两个字符串来查找,如果找到返回坐标,找不到返回-1,因为-1不可能出现在联系人的坐标中。

void Delpeoinfo(Contact* pc)//删除联系人 { assert(pc); char name[MAX_NAME]; printf("请输入你要删除的联系人:"); scanf("%s", name); /*if(strcmp(name,pc->data ))*/ int pos = Find(pc, name);//查找联系人,找到返回坐标,找不到返回-1 if (pos == -1) { printf("没有这个联系人\n"); } else { int i = 0; for (i = pos; i sz - 1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功\n"); } if ((pc->capacity - pc->sz) > 3) { peoinfo* ptr = (peoinfo*)realloc(pc->data, (pc->sz + 2) * sizeof(peoinfo)); if (ptr != NULL) { pc->data = ptr; pc->capacity = pc->sz + 2; printf("通讯录空闲内存大于三个人,减为两个空闲,减容成功\n"); } else { perror("Delpeoinfo"); } } }

这里删除之后我添加了一个内存删减的动作,当一口气删除很多人会导致有一大部分的内存被开辟但是还没被使用,所以我使用realloc来重新分配动态内存,这里的触发条件可以自行决断,我默认的是不能大于三个人的空闲位置,每次成功删除都应该总数减1,sz-1。

5.查找联系人

这里的查找其实和删除里面的差不多,通过名字查找,然后打印出这个人的信息,代码如下:

void Findpeoinfo_By_Name(const Contact* pc)//查找联系人 { assert(pc); char name[MAX_NAME]; printf("请输入你要查找的联系人:"); scanf("%s", name); int pos = Find(pc, name);//查找联系人,找到返回坐标,找不到返回-1 if (pos == -1) { printf("没有这个联系人\n"); } else { printf("%-10s %-5s %-5s %-20s %-20s\n", "名字", "性别", "年龄", "电话", "住址"); printf("%-10s %-5s %-5d %-20s %-20s\n", pc->data[pos].name, pc->data[pos].sex, pc->data[pos].age, pc->data[pos].tele, pc->data[pos].addr); } } 6.更改联系人信息

这里也要先调用删除模块中的查找,先找到联系人,然后在这个联系人的位置重新录入一遍信息即可,代码如下:

void Modifypeoinfo(Contact* pc)//修改联系人 { assert(pc); char name[MAX_NAME]; printf("请输入你要修改的联系人:"); scanf("%s", name); int pos = Find(pc, name);//查找联系人,找到返回坐标,找不到返回-1 if (pos == -1) { printf("没有这个联系人\n"); } else { printf("请输入修改后名字:>"); scanf("%s", pc->data[pos].name); printf("请输入修改后性别:>"); scanf("%s", pc->data[pos].sex); printf("请输入修改后年龄:>"); scanf("%d", &pc->data[pos].age); printf("请输入修改后号码:>"); scanf("%s", pc->data[pos].tele); printf("请输入修改后地址:>"); scanf("%s", pc->data[pos].addr); printf("修改成功\n"); } } 7.给通讯录排序

这个模块其实我个人认为不是很重要,但是还是写了一个版本供大家参考一下。 首先使用qsort这个库函数来进行排序,我这里通过名字和年龄,也可以使用其他可以比较大小的来排序,比如地址等,这里就不多赘述了。直接看代码:

static int sort_by_name(const void* elem1, const void* elem2) { //return *((struct peoinfo*)elem1)->name - *((struct peoinfo*)elem2)->name;//这个比较的为名字的第一个字符 //return ((struct peoinfo*)elem1)->name - ((struct peoinfo*)elem2)->name;//这个比较的两个字符串的首地址 return strcmp(((struct peoinfo*)elem1)->name, ((struct peoinfo*)elem2)->name); } static int sort_by_age(const void* elem1, const void* elem2) { return ((struct peoinfo*)elem1)->age - ((struct peoinfo*)elem2)->age; } static void Sortmenu() { printf("*********************\n"); printf(" 1,name \n"); printf(" 2,age \n"); printf("*********************\n"); } void Sort(Contact* pc)//使用qsort进行排序 { //qsort(void* base, // size_t num, // size_t width, // int(__cdecl * compare)(const void* elem1, const void* elem2)); int num = 0; //int input = 0;//main函数中还有相同名字的局部变量未释放,会造成堆栈的损坏,所以改为num Sortmenu(); printf("请选择以什么来排序:>"); scanf("%d", &num); if (2 == num) qsort(pc->data, pc->sz, sizeof(struct peoinfo), sort_by_age); else qsort(pc->data, pc->sz, sizeof(struct peoinfo), sort_by_name); printf("排序后\n"); printf("------------------------通讯录--------------------------\n"); printf("%-10s %-5s %-5s %-20s %-20s\n", "名字", "性别", "年龄", "电话", "住址"); int i = 0; for (i = 0; i sz; i++) { printf("%-10s %-5s %-5d %-20s %-20s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tele, pc->data[i].addr); printf("\n"); } printf("--------------------------------------------------------\n"); }

这个函数需要自己传入比较大小的方式,就是自己写个函数来传给这个库函数,比如:sort_by_name和sort_by_age这两个我写的通过名字和年龄来比较的函数, void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) ); 第一个参数为你需要排序的数据的首地址 第二个参数为多少个元素 第三个参数是每个元素占多大字节 第四也是关键的就是你需要通过什么样的方式来比较大小进行排序,让后传入你写的比较方式,元素1大于元素2返回1,等于返回0,小于返回-1,具体可以参考我上面写的两种比较方式。

8.销毁通讯录

使用完毕之后,应该销毁动态内存开辟的空间,防止动态内存,代码如下:

void DestroyContatc(Contact* pc)//销毁通讯录,回收动态内存 { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; } 三、源码

如下:分为一个头文件,两个源文件: Contact.h

#pragma once #pragma once #include #include #include #include #define MAX_NAME 20 #define MAX_ADDR 30 #define MAX_SEX 3 #define MAX_TELE 12 #define MAX_PEONUM 3 #define MAX_SZ 2 typedef struct peoinfo //联系人信息 { char name[MAX_NAME]; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; int age; }peoinfo; //typedef struct Contact //通讯录 //{ // peoinfo data[MAX_PEONUM];//数据 // int sz;//联系人个数 //}Contact; typedef struct Contact //动态内存通讯录 { peoinfo* data;//动态内存的首地址 int sz;//联系人个数 int capacity;//最大容量 }Contact; void InitContact(Contact* pc);//初始化通讯录 void Addpeoinfo(Contact* pc);//增加联系人 void PrintContact(const Contact* pc);//打印通讯录 void Delpeoinfo(Contact* pc);//删除联系人 void Findpeoinfo_By_Name(const Contact* pc);//查找联系人 void Modifypeoinfo(Contact* pc);//修改联系人 void Sort(Contact* pc);//排序 void DestroyContatc(Contact* pc);//销毁通讯录,回收动态内存 void SaveContact(Contact* pc);//保存通讯录 void LoadContact(Contact* pc);//加载通讯录 void Check_Capacity(Contact* pc);//判断容量

Contact.c

#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #include"contact.h" void LoadContact(Contact* pc)//加载通讯录 { FILE* pf = fopen("Contact.txt", "r");//以读的方式打开一个文件 if (pf == NULL) { perror("LoadContact"); return; } peoinfo temp = { 0 }; while (fread(&temp, sizeof(peoinfo), 1, pf)) { Check_Capacity(pc); pc->data[pc->sz] = temp; pc->sz++; } /*peoinfo* pa = pc->data; fread(pa,sizeof(peoinfo), 100, pf);*/ fclose(pf);//关闭文件 pf = NULL; } void InitContact(Contact* pc)//初始化动态通讯录 { pc->data = (peoinfo*)calloc(MAX_PEONUM, sizeof(peoinfo));//在堆区分配一块有MAX_PEONUM*sizeof(peoinfo)的一块内存,并且每个字节初始化为0 pc->sz = 0; pc->capacity = MAX_PEONUM; LoadContact(pc);//加载通讯录 } //void Addpeoinfo(Contact* pc)//增加联系人 //{ // if (pc->sz == MAX_PEONUM) // { // printf("通讯录已满\n"); // } // else // { // printf("请输入名字:>"); // scanf("%s", pc->data[pc->sz].name); // printf("请输入性别:>"); // scanf("%s", pc->data[pc->sz].sex); // printf("请输入年龄:>"); // scanf("%d", &pc->data[pc->sz].age); // printf("请输入号码:>"); // scanf("%s", pc->data[pc->sz].tele); // printf("请输入地址:>"); // scanf("%s", pc->data[pc->sz].addr); // pc->sz++; // // printf("添加成功\n"); // } //} void Addpeoinfo(Contact* pc)//增加联系人(动态内存) { Check_Capacity(pc); printf("请输入名字:>"); scanf("%s", pc->data[pc->sz].name); printf("请输入性别:>"); scanf("%s", pc->data[pc->sz].sex); printf("请输入年龄:>"); scanf("%d", &pc->data[pc->sz].age); 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)//打印通讯录 { printf("------------------------通讯录--------------------------\n"); printf("%-10s %-5s %-5s %-20s %-20s\n", "名字", "性别", "年龄", "电话", "住址"); int i = 0; for (i = 0; i sz; i++) { printf("%-10s %-5s %-5d %-20s %-20s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tele, pc->data[i].addr); printf("\n"); } printf("--------------------------------------------------------\n"); } static int Find(const Contact* pc, const char* name)//查找联系人,找到返回坐标,找不到返回-1 { assert(name && pc); int i = 0; for (i = 0; i sz; i++) { if (strcmp(name, pc->data[i].name) == 0) return i;//找到返回下标 } return -1; } void Delpeoinfo(Contact* pc)//删除联系人 { assert(pc); char name[MAX_NAME]; printf("请输入你要删除的联系人:"); scanf("%s", name); /*if(strcmp(name,pc->data ))*/ int pos = Find(pc, name);//查找联系人,找到返回坐标,找不到返回-1 if (pos == -1) { printf("没有这个联系人\n"); } else { int i = 0; for (i = pos; i sz - 1; i++) { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功\n"); } if ((pc->capacity - pc->sz) > 3) { peoinfo* ptr = (peoinfo*)realloc(pc->data, (pc->sz + 2) * sizeof(peoinfo)); if (ptr != NULL) { pc->data = ptr; pc->capacity = pc->sz + 2; printf("通讯录空闲内存大于三个人,减为两个空闲,减容成功\n"); } else { perror("Delpeoinfo"); } } } void Findpeoinfo_By_Name(const Contact* pc)//查找联系人 { assert(pc); char name[MAX_NAME]; printf("请输入你要查找的联系人:"); scanf("%s", name); int pos = Find(pc, name);//查找联系人,找到返回坐标,找不到返回-1 if (pos == -1) { printf("没有这个联系人\n"); } else { printf("%-10s %-5s %-5s %-20s %-20s\n", "名字", "性别", "年龄", "电话", "住址"); printf("%-10s %-5s %-5d %-20s %-20s\n", pc->data[pos].name, pc->data[pos].sex, pc->data[pos].age, pc->data[pos].tele, pc->data[pos].addr); } } void Modifypeoinfo(Contact* pc)//修改联系人 { assert(pc); char name[MAX_NAME]; printf("请输入你要修改的联系人:"); scanf("%s", name); int pos = Find(pc, name);//查找联系人,找到返回坐标,找不到返回-1 if (pos == -1) { printf("没有这个联系人\n"); } else { printf("请输入修改后名字:>"); scanf("%s", pc->data[pos].name); printf("请输入修改后性别:>"); scanf("%s", pc->data[pos].sex); printf("请输入修改后年龄:>"); scanf("%d", &pc->data[pos].age); printf("请输入修改后号码:>"); scanf("%s", pc->data[pos].tele); printf("请输入修改后地址:>"); scanf("%s", pc->data[pos].addr); printf("修改成功\n"); } } static int sort_by_name(const void* elem1, const void* elem2) { //return *((struct peoinfo*)elem1)->name - *((struct peoinfo*)elem2)->name;//这个比较的为名字的第一个字符 //return ((struct peoinfo*)elem1)->name - ((struct peoinfo*)elem2)->name;//这个比较的两个字符串的首地址 return strcmp(((struct peoinfo*)elem1)->name, ((struct peoinfo*)elem2)->name); } static int sort_by_age(const void* elem1, const void* elem2) { return ((struct peoinfo*)elem1)->age - ((struct peoinfo*)elem2)->age; } static void Sortmenu() { printf("*********************\n"); printf(" 1,name \n"); printf(" 2,age \n"); printf("*********************\n"); } void Sort(Contact* pc)//使用qsort进行排序 { //qsort(void* base, // size_t num, // size_t width, // int(__cdecl * compare)(const void* elem1, const void* elem2)); int num = 0; //int input = 0;//main函数中还有相同名字的局部变量未释放,会造成堆栈的损坏,所以改为num Sortmenu(); printf("请选择以什么来排序:>"); scanf("%d", &num); if (2 == num) qsort(pc->data, pc->sz, sizeof(struct peoinfo), sort_by_age); else qsort(pc->data, pc->sz, sizeof(struct peoinfo), sort_by_name); printf("排序后\n"); printf("------------------------通讯录--------------------------\n"); printf("%-10s %-5s %-5s %-20s %-20s\n", "名字", "性别", "年龄", "电话", "住址"); int i = 0; for (i = 0; i sz; i++) { printf("%-10s %-5s %-5d %-20s %-20s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tele, pc->data[i].addr); printf("\n"); } printf("--------------------------------------------------------\n"); } void DestroyContatc(Contact* pc)//销毁通讯录,回收动态内存 { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; } void SaveContact(Contact* pc)//保存通讯录 { FILE* pf = fopen("Contact.txt", "w");//以写的方式打开一个文件 if (pf == NULL) { perror("SaveContact"); return; } //int i = 0; //for (i = 0; i < pc->sz; i++) //{ // fwrite(pc->data+i, sizeof(peoinfo), 1 , pf);//将联系人的信息写入一个文件 //} fwrite(pc->data, sizeof(peoinfo), pc->sz, pf); fclose(pf);//关闭文件 pf = NULL; } void Check_Capacity(Contact* pc) { if (pc->sz == pc->capacity) { peoinfo* ptr = (peoinfo*)realloc(pc->data, (pc->capacity + MAX_SZ) * sizeof(peoinfo)); if (ptr != NULL) { pc->data = ptr; pc->capacity += MAX_SZ; printf("增容成功\n"); } else { perror("Addpeoinfo"); printf("通讯录已满,增容失败无法添加\n"); return; } } }

test.c

#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #include"contact.h" //通讯录 //1,增加联系人 //2,删除联系人 //3,查找联系人 //4,排序 //5,修改联系人 //6,打印通讯录 void menu() { printf("****************************\n"); printf("**** 1,add 2, del ****\n"); printf("**** 3,find 4,sort ****\n"); printf("**** 5,modify 6,print ****\n"); printf("**** 0,exit ****\n"); printf("****************************\n"); } enum option { EXIT, ADD, DEL, FIND, SORT, MODIFY, PRINT }; int main() { Contact con; InitContact(&con); printf("欢迎使用通讯录\n"); int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case ADD: Addpeoinfo(&con); break; case DEL: Delpeoinfo(&con); break; case FIND: Findpeoinfo_By_Name(&con); break; case SORT: Sort(&con); break; case MODIFY: Modifypeoinfo(&con); break; case PRINT: PrintContact(&con); break; case EXIT: SaveContact(&con); DestroyContatc(&con); printf("退出程序\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); return 0; } 四,总结

希望能够帮到你 beybey 在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有