通讯录

文章目录

  • 1. 基本思路
  • 2.代码实现
    • 2.1 定义各种**宏**和**结构体**。
    • 2.2 创建结构体并进行初始化
    • 2.3 打印菜单,模拟用户的选择
    • 2.4 增加联系人
    • 2.5 删除联系人
    • 2.6 查找联系人
    • 2.7 修改联系人
    • 2.8 对通讯录进行升序排序
    • 2.9 打印通讯录
    • 2.10 结束程序并销毁通讯录
  • 3. 文件改进版本
    • 3.1 每次关闭通讯录将信息保存到文件中
    • 3.2 每次初始化通讯录后将文件中信息加载到通讯录上
  • 4.程序整体代码

1. 基本思路

  1. 用宏定义经常出现的变量,如数组的大小,通讯录的初识容量等。
  2. 定义两个结构体,用一个结构体记录某一个联系人的所有信息,另外一个结构体记录所有的联系人,当前通讯录的大小以及通讯录能存多少人。
  3. 通讯录的功能有增删差改,排序,以及打印。
  4. 文章中文字解释较少,主要解释都在代码中。

2.代码实现

2.1 定义各种结构体


// 用宏替换后续经常出现的数字,便于程序的维护
#define NAME_MAX 20  // 名字的字符串的最大长度
#define SEX_MAX 20   // 性别的字符串的最大长度
#define TELE_MAX 20  // 电话的最大字符串的长度
#define ADDR_MAX 20  // 住址的最大字符串的长度#define CON_INIT 3 // 通讯录的初识大小// 定义每个人的信息为一个结构体,同时为了方便表示,采用typedef
typedef struct PeoInfo
{char name[NAME_MAX]; // 每个人的姓名char sex[SEX_MAX];   // 每个人的性别int age;             // 每个人的年龄char tele[TELE_MAX]; // 每个人的电话char addr[ADDR_MAX]; // 每个人的住址
}PeoInfo;// 定义整个通讯录为一个结构体,同时为了方便表示,采用typedef
typedef struct Contact
{// 为了节省空间,采用动态内存分配的方法来获取整个通讯录的空间PeoInfo* data; // 结构体指针,可通过运算访问到所有人的信息int size; // 表示当前通讯录存了多少人的信息int capacity; // 表示该通讯录总共可以存多少人的信息
}Contact;

2.2 创建结构体并进行初始化

// 定义一个通讯录结构体Contact con;// 对该通讯录结构体进行初始化// 此后应该传结构体的地址而不是结构体,原因是要对结构体作出修改// 并且传结构体的地址节省空间且效率更高InitContact(&con);


// 在这个函数内部对通讯录结构体进行初始化
void InitContact(Contact* pc)
{// 此时通讯录是空的,应先为通讯录分配空间pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CON_INIT);// 如果分配成功,将通讯录的size设为0,capacity设为初识大小pc->size = 0;pc->capacity = CON_INIT;
}

2.3 打印菜单,模拟用户的选择

菜单如下:


void menu()
{printf("请输入你的选择\n");printf("***** 1. add     2. del    *****\n");printf("***** 3. search  4. modify *****\n");printf("***** 5. sort    6. print  *****\n");printf("*****        0. exit       *****\n");return;
}

用户的选择功能如下

// 定义input,用户输入input进行选择int input;do{menu(); // 打印菜单,提示用户进行选择scanf("%d", &input); // 用户输入input进行选择switch (input) // 用户选择后进入不同的功能{case ADD: // 增加联系人AddContact(&con);break;case DEL: // 删除联系人DeleteContact(&con);break;case SEARCH: // 查找联系人SearchContact(&con);break;case MODIFY: // 改变联系人ModifyContact(&con);break;case SORT:  // 对联系人进行排序SortContact(&con);break;case PRINT: // 打印所有联系人的信息PrintContact(&con);break;case EXIT: // 退出程序并销毁DestroyContact(&con);break;}} while (input);

用枚举常量来表示各种选项

2.4 增加联系人

在每次增加联系人前,都要进行检查是否需要扩容的操作,若需要则进行扩容

void AddContact(Contact* pc)
{// 判断通讯录是否已满,若满,进行扩容if (pc->size == pc->capacity){PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 2);if (tmp == NULL){printf("realloc fail\n");return;}pc->data = tmp;// 若扩容成功,增大capacitypc->capacity *= 2;}// 输入要添加的联系人的信息// 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息printf("请输入名字\n");scanf("%s", pc->data[pc->size].name);printf("请输入性别\n");scanf("%s", pc->data[pc->size].sex);printf("请输入年龄\n");scanf("%d", &pc->data[pc->size].age);printf("请输入电话\n");scanf("%s", pc->data[pc->size].tele);printf("请输入住址\n");scanf("%s", pc->data[pc->size].addr);pc->size++; // 将存入的联系人的数量加1// 添加成功后,向用户展示新的通讯录PrintContact(pc);
}

2.5 删除联系人


void DeleteContact(Contact* pc)
{printf("请输入要删除的联系人的名字\n");char name[20];scanf("%s", name);// 定义一个新函数find,用来查找是否有这个联系人// 如果有,返回联系人的下标,如果没有,返回-1int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{for (int i = ret; i < pc->size - 1; i++){pc->data[i] = pc->data[i + 1];}pc->size--;}return;
}

这里还要定义一个find函数来找该联系人,如果找到返回该联系人的下标,如果找不到则返回-1。


int find(Contact* pc, char* name)
{for (int i = 0; i < pc->size; i++){if (strcmp(name, pc->data[i].name) == 0){return i;}}return -1;
}

2.6 查找联系人

查找时利用find函数来查找


void SearchContact(Contact* pc)
{printf("请输入要查找的联系人的名字\n");char name[20];scanf("%s", name);// 利用已经定义的find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{// 如果找到,打印该联系人的信息,首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[ret].name,pc->data[ret].sex,pc->data[ret].age,pc->data[ret].tele,pc->data[ret].addr);}return;
}

2.7 修改联系人


void ModifyContact(Contact* pc)
{printf("请输入要修改的联系人的名字\n");char name[20];scanf("%s", name);// 利用find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{printf("请输入名字\n");scanf("%s", pc->data[ret].name);printf("请输入性别\n");scanf("%s", pc->data[ret].sex);printf("请输入年龄\n");scanf("%d", &pc->data[ret].age);printf("请输入电话\n");scanf("%s", pc->data[ret].tele);printf("请输入住址\n");scanf("%s", pc->data[ret].addr);}return;
}

2.8 对通讯录进行升序排序

void SortContact(Contact* pc)
{// 这里采用升序排列// 且采用冒泡排序的方式进行排列for (int i = 0; i < pc->size; i++){for (int j = 0; j < pc->size - 1 - i; i++){if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0){char tmp[20];strcpy(tmp, pc->data[j].name);strcpy(pc->data[j].name, pc->data[j + 1].name);strcpy(pc->data[j + 1].name, tmp);}}}printf("排序成功\n");
}

2.9 打印通讯录


// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{// 首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");// 然后用for循环打印所有联系人的信息for (int i = 0; i < pc->size; i++){printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}
}

2.10 结束程序并销毁通讯录

void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->size = 0;pc->capacity = 0;
}

3. 文件改进版本

利用文件实现
当通讯录退出的时候,把信息写到文件。
当通讯录初始化后,加载文件的信息到通讯录中。

3.1 每次关闭通讯录将信息保存到文件中

在每次销毁通讯录之前将通讯录的信息保存到文件中

3.2 每次初始化通讯录后将文件中信息加载到通讯录上



CheckCapacity函数中的内容直接拷贝AddContact函数中的数组扩容即可

4.程序整体代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>// 用宏替换后续经常出现的数字,便于程序的维护
#define NAME_MAX 20  // 名字的字符串的最大长度
#define SEX_MAX 20   // 性别的字符串的最大长度
#define TELE_MAX 20  // 电话的最大字符串的长度
#define ADDR_MAX 20  // 住址的最大字符串的长度#define CON_INIT 3 // 通讯录的初识大小// 定义每个人的信息为一个结构体,同时为了方便表示,采用typedef
typedef struct PeoInfo
{char name[NAME_MAX]; // 每个人的姓名char sex[SEX_MAX];   // 每个人的性别int age;             // 每个人的年龄char tele[TELE_MAX]; // 每个人的电话char addr[ADDR_MAX]; // 每个人的住址
}PeoInfo;// 定义整个通讯录为一个结构体,同时为了方便表示,采用typedef
typedef struct Contact
{// 为了节省空间,采用动态内存分配的方法来获取整个通讯录的空间PeoInfo* data; // 结构体指针,可通过运算访问到所有人的信息int size; // 表示当前通讯录存了多少人的信息int capacity; // 表示该通讯录总共可以存多少人的信息
}Contact;// 使用枚举常量来表示用户的各种选择
enum Option
{EXIT,ADD,DEL,SEARCH,MODIFY,SORT,PRINT
};void SaveContact(Contact* pc)
{// 以读的形式打开文件FILE* pf = fopen("contact.txt", "wb");if (pf == NULL){perror("fopen");return;}// 将通讯录以二进制的形式保存到文件中// 由于联系人的信息有很多,所以这里可以采用for循环for (int i = 0; i < pc->size; i++){fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);}// 关闭文件fclose(pf);pf = NULL;
}CheckCapacity(Contact* pc)
{// 判断通讯录是否已满,若满,进行扩容if (pc->size == pc->capacity){PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 2);if (tmp == NULL){printf("realloc fail\n");return;}pc->data = tmp;// 若扩容成功,增大capacitypc->capacity *= 2;}
}void LoadContact(Contact* pc)
{// 以读的形式打开文件FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("fopen");return;}// 将文件中的内容加载到通讯录中// 这里用fread函数,当fread函数读取的联系人信息数为0时,说明读取结束PeoInfo tmp = { 0 }; // 定义一个联系人信息的结构体,便于读取while (fread(&tmp, sizeof(PeoInfo), 1, pf)){CheckCapacity(pc); // 在这个函数中查看数组是否需要扩容,若需要,则扩容pc->data[pc->size] = tmp;pc->size++;}// 关闭文件fclose(pf);pf = NULL;return;
}// 在这个函数内部对通讯录结构体进行初始化
void InitContact(Contact* pc)
{// 此时通讯录是空的,应先为通讯录分配空间pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CON_INIT);// 如果分配成功,将通讯录的size设为0,capacity设为初识大小pc->size = 0;pc->capacity = CON_INIT;LoadContact(pc);return;
}void menu()
{printf("请输入你的选择\n");printf("***** 1. add     2. del    *****\n");printf("***** 3. search  4. modify *****\n");printf("***** 5. sort    6. print  *****\n");printf("*****        0. exit       *****\n");return;
}// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{// 首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");// 然后用for循环打印所有联系人的信息for (int i = 0; i < pc->size; i++){printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}
}void AddContact(Contact* pc)
{CheckCapacity(pc);// 输入要添加的联系人的信息// 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息printf("请输入名字\n");scanf("%s", pc->data[pc->size].name);printf("请输入性别\n");scanf("%s", pc->data[pc->size].sex);printf("请输入年龄\n");scanf("%d", &pc->data[pc->size].age);printf("请输入电话\n");scanf("%s", pc->data[pc->size].tele);printf("请输入住址\n");scanf("%s", pc->data[pc->size].addr);pc->size++; // 将存入的联系人的数量加1// 添加成功后,向用户展示新的通讯录PrintContact(pc);
}int find(Contact* pc, char* name)
{for (int i = 0; i < pc->size; i++){if (strcmp(name, pc->data[i].name) == 0){return i;}}return -1;
}void DeleteContact(Contact* pc)
{printf("请输入要删除的联系人的名字\n");char name[20];scanf("%s", name);// 定义一个新函数find,用来查找是否有这个联系人// 如果有,返回联系人的下标,如果没有,返回-1int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{for (int i = ret; i < pc->size - 1; i++){pc->data[i] = pc->data[i + 1];}pc->size--;}return;
}void SearchContact(Contact* pc)
{printf("请输入要查找的联系人的名字\n");char name[20];scanf("%s", name);// 利用已经定义的find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{// 如果找到,打印该联系人的信息,首先打印五个标题printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",pc->data[ret].name,pc->data[ret].sex,pc->data[ret].age,pc->data[ret].tele,pc->data[ret].addr);}return;
}void ModifyContact(Contact* pc)
{printf("请输入要修改的联系人的名字\n");char name[20];scanf("%s", name);// 利用find函数进行查找int ret = find(pc, name);if (ret == -1){printf("没有找到该联系人\n");}else{printf("请输入名字\n");scanf("%s", pc->data[ret].name);printf("请输入性别\n");scanf("%s", pc->data[ret].sex);printf("请输入年龄\n");scanf("%d", &pc->data[ret].age);printf("请输入电话\n");scanf("%s", pc->data[ret].tele);printf("请输入住址\n");scanf("%s", pc->data[ret].addr);}return;
}void SortContact(Contact* pc)
{// 这里采用升序排列// 且采用冒泡排序的方式进行排列for (int i = 0; i < pc->size; i++){for (int j = 0; j < pc->size - 1 - i; i++){if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0){char tmp[20];strcpy(tmp, pc->data[j].name);strcpy(pc->data[j].name, pc->data[j + 1].name);strcpy(pc->data[j + 1].name, tmp);}}}printf("排序成功\n");
}void DestroyContact(Contact* pc)
{free(pc->data);pc->data = NULL;pc->size = 0;pc->capacity = 0;
}int main()
{// 定义一个通讯录结构体Contact con;// 对该通讯录结构体进行初始化// 此后应该传结构体的地址而不是结构体,原因是要对结构体作出修改// 并且传结构体的地址节省空间且效率更高InitContact(&con); // 定义input,用户输入input进行选择int input;do{menu(); // 打印菜单,提示用户进行选择scanf("%d", &input); // 用户输入input进行选择switch (input) // 用户选择后进入不同的功能{case ADD: // 增加联系人AddContact(&con);break;case DEL: // 删除联系人DeleteContact(&con);break;case SEARCH: // 查找联系人SearchContact(&con);break;case MODIFY: // 改变联系人ModifyContact(&con);break;case SORT:  // 对联系人进行排序SortContact(&con);break;case PRINT: // 打印所有联系人的信息PrintContact(&con);break;case EXIT: // 退出程序并销毁//在销毁之前将通讯录内的信息保存到文件中SaveContact(&con);DestroyContact(&con);break;}} while (input);return 0;
}

C语言 - 通讯录详解相关推荐

  1. python语言编程基础-Python语言入门详解!快速学成Python!

    原标题:Python语言入门详解!快速学成Python! 很多技能是被职场所需要的,但很可惜... 这些技能在大学中并学习不到. 大学和职场现实存在的横沟对大部分同学来说难以跨越或碰得头破血流... ...

  2. python语言入门m-Python语言入门详解!快速学成Python!

    今日主题 "Python语言入门详解" 近两年来,Python语言借着数据科学和人工智能的"东风"成为了最流行的编程语言--街头巷尾人们口口相传.同时,Pyth ...

  3. C语言之详解#ifdef等宏

    C语言之详解#ifdef等宏 这几个宏是为了进行条件编译.一般情况下,源程序中所有的行都参加编译.但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是&qu ...

  4. c语言 宏教程 pdf,C语言之详解_ifdef等宏.pdf

    C 语言之详解 #ifdef 等宏 指令用途 #空指令,无任何效果 #include 包含一个源代码文件 #define 定义宏 #undef 取消已定义的宏 #if 如果给定条件为真,则编译下面代码 ...

  5. 代码检查规则:Python语言案例详解

    在之前的文章中代码检查规则:Java语言案例详解学习了Java的检查规则.我们今天将学习<代码检查规则:Python语言案例详解>,内容主要分为两个部分:Python的代码检查规则和Pyt ...

  6. 代码检查规则:Java语言案例详解

    本节课程为<代码检查规则:Java语言案例详解>, 通常情况下Java的代码检查规则可以分为以下十类: 接下来,让我们具体来看看每个分类的内容. 一.源文件规范 该类规范主要从文件名.文件 ...

  7. Linux_arm_启动_c语言部分详解,[原创]Linux arm 启动 c语言部分详解第四讲

    Linux arm启动c语言部分详解第四讲(from setup_per_cpu_areas();) Written by leeming 上面的setup_arch花了我们大量的篇幅,现在我们要继续 ...

  8. c 语言中 %是什么运算符,C 语言基础----详解C中的运算符

    C语言中又有哪些运算符呢? 如下所示: ※ 算术运算符 ※ 赋值运算符 ※ 关系运算符 ※ 逻辑运算符 ※ 三目运算符 C语言基本算术运算符如下表: 除法运算中注意: 如果相除的两个数都是整数的话,则 ...

  9. python切片输出_Python语言之详解切片

    本篇文章主要讲述Python语言之详解切片,希望阅读本篇文章以后大家有所收获,帮助大家对相关内容的理解更加深入. 切片操作就是对list,元组,字符串进行截取操作有了切片操作,很多地方循环就不再需要了 ...

最新文章

  1. python 从网络URL读取图片并直接处理的代码
  2. 《SAP HANA平台应用开发》—第3章3.1节信息建模
  3. 题目1184:二叉树遍历
  4. js实现网页页面回到顶部
  5. C++阶段01笔记汇总【C++软件安装、C++初识、数据类型、运算符、程序流程结构、数组、函数、指针、结构体】
  6. java也可以做黑客?
  7. 58同城 php,58同城PHP面试试题
  8. 3dmax su 简单_sketchup导入3Dmax技巧
  9. EasyUI下拉框自适应高度
  10. SDNU_ACM_ICPC_2020_Winter_Practice_4th [Reproduced]
  11. VMware虚拟机下载与安装(内附密钥)
  12. 三年级计算机课可以画的图有,三年级上册画画图片
  13. 阿里达摩院的AI Earth(AIE)初体验
  14. 青柠疫服自动打卡脚本
  15. Linux之 解决 Linux 性能瓶颈的黄金 60 秒
  16. ArcGIS制图——单图层道路压盖处理
  17. 孩子学习机怎么买?3款爆款学习机详细测评
  18. 2019年软考及格分数线
  19. 2021年全球陀螺测斜仪收入大约6百万美元,预计2028年达到7百万美元
  20. python3 使用fasttext 进行文本分类(一定要用linux )

热门文章

  1. 如何在线签订电子合同, 看这一篇就够了!
  2. JVM类加载机制简单介绍
  3. 查询公司的DUNS 邓白氏编码
  4. GEE Google Earth Engine 上传矢量shp数据报错
  5. 端游已死?不止如此,PC可能也命不久矣
  6. 建网站并不难,只需6个步骤,就能做出一个网站
  7. 授之以渔——我读《应需而变》
  8. 第一份工作应该做什么???
  9. javaweb实现简单注册登录功能——(注册)
  10. 网络安全入行?来了解下网络安全从业人员类别及其工作任务