C语言|C语言项目实践——通讯录(三版本联合分解)


待到秋来九月八,我花开后百花杀

  • 项目简介
  • 项目思路
    • 流程图
    • 项目用到的知识点
  • 项目功能的实现
    • 函数声明及抽象目标
    • 主函数的架构与搭建
    • 初始化通讯录
    • 加载联系人
    • 添加成员
    • 打印通讯录
    • 删除成员
    • 查找成员
    • 清空通讯录
    • 摧毁通讯录(动态版本)
    • 保存联系人到文件(文件版本)
  • 所遇问题
    • 解决办法
  • 项目源码
【C语言|C语言项目实践——通讯录(三版本联合分解)】
项目简介 近代通信少不了电话号码,但是朋友或是要联络的人太多,就需要把电话号码记下来,然而书本纸面上的记载,查询都太过麻烦,因此我们可以利用计算机编程实现一个多功能全面的通讯录,帮助我们更加方便的处理这些电话号码。
目标功能涵盖:
  1. 添加联系人信息
  2. 删除指定联系人信息
  3. 查找指定联系人信息
  4. 显示所有联系人信息
  5. 清空所有联系人
  6. 以名字排序所有联系人
  7. 保存联系人到文件 (文件版)
  8. 从文件加载联系人(文件版)
共需完成三种版本:
  1. 结构体版本(基础版本)
  2. 动态内存版本
  3. 文件操作版本
项目思路 流程图 主要功能放在一个对应的函数里进行封装
C语言|C语言项目实践——通讯录(三版本联合分解)
文章图片

C语言|C语言项目实践——通讯录(三版本联合分解)
文章图片

项目用到的知识点
  1. 结构体的使用
  2. 动态内存malloc realloc calloc free 的使用
  3. 文件的操作
项目功能的实现 函数声明及抽象目标 1.抽象个人信息
typedef struct PersonInfo { char name[MAX_NAME]; char sex[SEX_NAME]; short age; char tele[TEL_NAME]; char addr[TEL_NAME]; }PersonInfo;

2.抽象通讯录
typedef struct Contact { //PersonInfo per[MAX_PER_NUM] ; 静态版本 int usedsize; //有效数据个数 动态版本 PersonInfo* per; //动态版本 int capacity; //初始容量 }Contact; //通讯录

需要实现函数:
#ifndef __CONTACT_H__ #define __CONTACT_H__ #include #include #include #include enum Option { EXIT, ADD, DEL, SEARCH, MONDIFY, SHOW, EMPTY, SORT }; #define MAX_NAME 20 #define SEX_NAME 5 #define TEL_NAME 12 #define ADDR_NAME 20 //通讯录最多为1000人 #define MAX_PER_NUM 1000 //动态扩容版本 #define DEFAULT_SIZE 2; typedef struct PersonInfo { char name[MAX_NAME]; char sex[SEX_NAME]; short age; char tele[TEL_NAME]; char addr[TEL_NAME]; }PersonInfo; typedef struct Contact { //PersonInfo per[MAX_PER_NUM] ; 普通版本 PersonInfo* per; int usedsize; //有效数据个数 int capacity; //初始容量 }Contact; //通讯录 //初始化通讯录 void InitContact(Contact *con); //添加成员 void AddContact(Contact *con); //打印通讯录 void ShowContact(Contact *con); //删除成员 void DelContact(Contact *con); //查找成员 int SearchContact(Contact *con); //清空通讯录 void EmptyContact(Contact *con); //摧毁通讯录(动态版本) void DestoryContact(Contact *con); //文件版本 //保存联系人到文件 void SaveContact(Contact *con); //加载联系人 void LoadContact(Contact *con); #endif //__CONTACT_H__

主函数的架构与搭建
#include"contact.h"void menu() { printf("******************通讯录********************\n"); printf("*** 1.add2.show3.delete 4.search ****\n"); printf("*** 5.empty 6.distory 7.save8.load****\n"); printf("***************** 0.quit *******************\n"); printf("********************************************\n"); }int main() { int select = 0; Contact contact; InitContact(&contact); do{ menu(); printf("Pleas intput you changce:"); scanf("%d", &select); switch (select) { case QUIT: printf("Good bye!\n"); break; case ADD: AddContact(&contact); break; case SHOW: ShowContact(&contact); break; case DEL: DelContact(&contact); break; case SEARCH: SearchContact(&contact); break; case EMPTY: EmptyContact(&contact); break; case DESTORY: DestoryContact(&contact); break; case SAVE: SaveContact(&contact); break; case LOAD: LoadContact(&contact); break; default: break; } } while (select); system("pause"); return 0; }

初始化通讯录
void InitContact(Contact *pcon) { //普通版本 //pcon->usedSize = 0; //memset(pcon->per,0,sizeof(pcon->per)); pcon->usedsize = 0; pcon->capacity = DEFAULT_SIZE; pcon->per = (PersonInfo *)malloc( sizeof(PersonInfo) * pcon->capacity); assert(pcon->per != NULL); LoadContact(pcon); //有可能文件中也有存储的联系人 }

加载联系人
void LoadContact(Contact *pcon) { FILE *pf = fopen("Contact.bat", "rb"); PersonInfo tmp = { 0 }; if (pf == NULL) { return; } //fread函数的返回值是:读取成功的字节数 while (fread(&tmp, sizeof(PersonInfo), 1, pf) > 0) { //必须判断是否为满,如果满了扩容 CheckFullAndRe(pcon); pcon->per[pcon->usedsize++] = tmp; } fclose(pf); pf = NULL; }

添加成员
void AddContact(Contact *pcon) { //普通版本,无扩容解决办法 //if(pcon->usedSize == MAX_NUMBER) //{ // printf("this contact is full\n"); // return; //} if (CheckFullAndRe(pcon) != 1) { printf("扩容失败\n"); return; } printf("请输入姓名:"); scanf("%s", pcon->per[pcon->usedsize].name); printf("请输入年龄:"); scanf("%d", &(pcon->per[pcon->usedsize].age)); printf("请输入性别:"); scanf("%s", pcon->per[pcon->usedsize].sex); printf("请输入电话:"); scanf("%s", pcon->per[pcon->usedsize].tele); printf("请输入住址:"); scanf("%s", pcon->per[pcon->usedsize].addr); pcon->usedsize++; printf("添加成功\n"); }

打印通讯录
void ShowContact(Contact *pcon) { int i = 0; printf("%-10s %-5s %-5s %-11s %-20s\n", "姓名", "年龄", "性别", "电话", "住址"); for (i = 0; i < pcon->usedsize; i++) { printf("%-10s %-5d %-5s %-11s %-20s\n", pcon->per[i].name, pcon->per[i].age, pcon->per[i].sex, pcon->per[i].tele, pcon->per[i].addr); } }

删除成员
void DelContact(Contact *pcon) { int index = SearchContact(pcon); int i = 0; if (index == -1) { printf("删除失败,查无此人\n"); return; } for (i = index; i < pcon->usedsize - 1; i++) { pcon->per[i] = pcon->per[i + 1]; } pcon->usedsize--; printf("删除成功\n"); }

查找成员
int SearchContact(Contact *pcon) { int i = 0; char name[MAX_NAME] = { 0 }; if (pcon->usedsize == 0) { printf("通讯录为空\n"); return -1; } printf("请输入你要删除的姓名:"); scanf("%s", name); for (i = 0; i < pcon->usedsize; i++) { if (strcmp(pcon->per[i].name, name) == 0) { return i; } } return -1; }

清空通讯录
void EmptyContact(Contact *pcon) { pcon->usedsize = 0; }

摧毁通讯录(动态版本)
void DestoryContact(Contact *pcon) { SaveContact(pcon); free(pcon->per); pcon->per = NULL; //预防野指针 pcon->capacity = 0; pcon->usedsize = 0; }

保存联系人到文件(文件版本)
void SaveContact(Contact *pcon) { int i = 0; FILE *pf = fopen("Contact.bat", "wb"); assert(pf != NULL); for (i = 0; i < pcon->usedsize; i++) { fwrite(pcon->per + i, sizeof(PersonInfo), 1, pf); } fclose(pf); pf = NULL; }

所遇问题 通讯录无论是静态版本还是动态版本,储存人数都有上限。
动态内存虽然可以需要多少开辟多少内存,但仍需要在编程中定义。
解决办法 设计一个拓展函数,如果存储不足时,再开辟更多内存。
扩容通讯录
static int CheckFullAndRe(Contact *pcon) { if (pcon->usedsize == pcon->capacity) { PersonInfo * ptr = NULL; ptr = (PersonInfo *)realloc(pcon->per, sizeof(PersonInfo) * pcon->capacity * 2); if (ptr != NULL) { pcon->per = ptr; pcon->capacity *= 2; printf("扩容成功\n"); return 1; } else { return 0; //扩容失败 } } return 1; }

项目源码 我的通讯录项目源码:https://github.com/GagaAutom/C-programming-language/blob/master/my_contact/contact

    推荐阅读