C语言详解【通讯录的实现】

news/2024/11/7 8:39:46/

前言:
在之前的学习中我们已经了解了结构体的一些知识,有了之前的知识的储备,在这里我们就可以尝试通讯录的实现。

目录

  • 问题描述
  • 基本流程
  • 前期的准备工作
  • 实现过程
    • 第一阶段
    • 第二阶段
    • 第三阶段
      • 1.增加联系人
      • 2.删除联系人
      • 3.打印通讯录
      • 4.查找指定联系人
      • 5.修改联系人
      • 6.排序
      • 7.销毁通讯录
  • 代码以及总结
    • test.c代码
    • contact.c代码
    • contact.h代码

问题描述

用c语言来进行通讯录的基本实现。

基本流程

实现通讯录的基本流程分为以下几步:
1,首先通讯录是用来存放联系人的信息,一个人信息有很多,例如(姓名,性别,年龄等),所以这里使用我们之前学过的结构体来存放联系人的信息。
2,一个通讯录所包含的联系人肯定不止一个,所以需要一个数组来存放,因为创建了联系人结构体的类型,所以需要使用相同结构体类型的数组来存放多个人的信息。(这里采用了动态内存的方式来存储联系人,传回来的为动态内存的首地址,也可以将其看为一个结构体类型的数组来使用)
3,通讯录的主要功能:增删查改联系人,打印通讯录,排序以及退出通讯录等功能。
增加联系人:往数组里面放元素。
删除联系人:找到联系人,将需要删除的联系人后面的联系人前移一位实现覆盖删除。
查找联系人:一般按名字查找。
修改联系人:找到联系人的位置,在相同位置重新录入信息实现覆盖修改。
打印通讯录:打印数组,把我们录入的展示出来。
联系人排序:选择用名字排序还是年龄排序或者其他信息排序,可以使用qsort函数来实现(这个函数在下期我会拿出来给大家细讲)。
退出:当我们完成我们想要的工作后便退出通讯录
4,使用动态内存开辟的方法来储存联系人,节约内存。
5,使用文件操作来保存信息和写入信息,使系统运行时自动载入已保存的信息。

前期的准备工作

首先我们还是在一个项目中创建三个文件,两个源文件,一个文件存放主函数,一个文件来存放通讯录的实现函数,还有一个头文件来声明函数。
在这里插入图片描述

实现过程

第一阶段

首先我们还是先搭建主体的框架,写一个菜单来实现相应的功能:

void menu()
{printf("**************************************\n");printf("*****   1. add      2. del       *****\n");printf("*****   3. search   4. modify    *****\n");printf("*****   5. show     6. sort      *****\n");printf("*****   0. exit                  *****\n");printf("**************************************\n");
}

上面主函数内部的分支循环 case 中我们输入的字符是为了便于理解,但是这样是没办法实现最开始的以数字来进行菜单选项的初衷的,所以我们需要枚举这些选择功能来达到这样的一个效果:

enum option
{ADD,DEL,SEARCH,MODIFY,SHOW,SORT,EXIT
};

写好之后我们可以看出结果如下:
在这里插入图片描述
菜单写出来之后,自然而然我们要让玩家能够根据信息输入相应的数字进行游戏,效果图如下:

do{menu();printf("请选择数字->");scanf("%d", &input);switch (input){case 1://cese ADD:AddContact(&per);break;case 2://cese DEL:DelContact(&per);break;case 3://cese SEARCH:SearchContact(&per);break;case 4://cese MODIFY:ModifyContact(&per);break;case 5://cese SHOW:ShowContact(&per);break;case 6://cese SORT:SortContact(&per);break;case 0://cese EXIT:DestroyContact(&per);break;default:printf("选择错误,请重新输入\n");break;}} while (input);

到此我们前期的框架基本上已经搭建好了。紧接着就是建立我们的通讯录了,对我们想要的功能进行依次的实现。

第二阶段

第一步:我们需要定义联系人的一些属性,定义了一个结构体PeoInfo,简单分析不难发现,这个结构体表示的就是一个人的信息。姓名、电话、地址、年龄、性别这几条信息存放在一个字符数组中:

struct PeoInfo
{char name[MAX_NAME];char sex[MAX_SEX];char tele[MAX_TELE];int age;char addr[MAX_ADDR];
};

第二步:就是定义通讯录的内容,而在这一步为了高效的实现的日常生活动态增长的一个情况,我们使用动态增长的一个版本。结构体有三个成员,一个是结构体数组,数组中的每个元素就是上面创建的结构体。还有一个元素是整型变量sz。上一个结构体代表的是一个人的信息,最后就是满足动态增长而设置的判断,而通讯录的目的就是把多个人的信息存放在一起,所以这个结构体Contact所代表的的就是我们的通讯录:

struct Contact
{struct PeoInfo* data;//int sz;              //int capacity;        //当前的容量
};

紧接着就是为了实现我们上面结构体内部的这些内容,我们需要进行一些声明:

#define MAX_NAME 20
#define MAX_SEX  10
#define MAX_TELE 12
#define MAX_ADDR 30
#define MAX 100#define DEFAULT_SZ 3
#define INC_SZ 2

到此对通讯录的建立就完成了,接下来我们要做的便是关键一步对通讯录进行相应的初始化操作以及功能实现

第三阶段

首先进行的是初始化操作(后面都以动态增长为例):
初始化函数InitContact,初始化通讯录,使用malloc(动态内存开辟函数)开辟动态内存来存放联系人的信息,sz为有效联系人个数,capacity为通讯录的最大容量

void InitContact(struct Contact* pc)
{assert(pc); //断言pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ *sizeof(struct PeoInfo));if (pc->data == NULL){printf("InitContact fail");return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;}

接下来就是实现相应功能的功能函数了。

1.增加联系人

//动态增长联系人
void AddContact(struct Contact* pc)
{assert(pc);if (0 == check_capacity(pc)){return;}//增加人的信息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。检查容量函数如下:

int  check_capacity(struct Contact* pc)
{if (pc->sz == pc->capacity){struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(struct PeoInfo));if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;printf("成功增容");return 1;}else{printf("AddContact fail");return 0;}}elsereturn 1;
}

2.删除联系人

删除之前需要找到联系人的位置,因为这个函数我们不用让外面看到,所以用static来修饰这个函数,这样的话这个函数就只能在该源文件内部使用。该函数接收两个参数,一个是通讯录结构体,一个是要查找的成员名称。这里通过名字查找代码如下:

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

查找的过程采用遍历的方式,让通讯录中的每一个成员的名字来和要查找的成员名字来作比较,这里用到字符串比较函数strcmp,当这两个字符串相等时,该函数返回值为0,第一个大于第二个返回一个大于0的值,小于则返回一个小于0的值。当判断函数的返回值等于0时,说明找到了要查找的元素,紧接着返回该元素的下标。如果找不到返回-1即可,在之前我们也已经介绍过这个函数。
主体函数如下:

//删除指定联系人
void DelContact(struct Contact* pc)
{char name[MAX_NAME];printf("请输入删除的人的姓名:->");scanf("%s", name);//判断输入是否合理int ret=FindByName(pc, name);if (ret == -1)printf("删除的人不存在,请重新输入\n");else{int j = 0;for (j = ret; j < pc->sz - 1; j++){pc->data[j] = pc->data[j + 1];}pc->sz--;printf("删除成功\n");}
}

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

3.打印通讯录

打印的时候我们可以估算一下各个信息的长度,然后打印。%10s的意思就是打印的空间占10个字符型空间。由于通讯录中可能有多条信息,所以打印的时候采用一个循环来打印,打印的次数就是有效信息数sz的值。

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

4.查找指定联系人

先输入要查找人的姓名,然后调用查找函数,观察返回值,如果找不到,就打印查找的人不存在。如果找到,返回这个人的下标,我们就可以通过这个人的下标来把这个人的信息展示出来。

//查找指定联系人
void SearchContact(const struct Contact* pc)
{char name[MAX_NAME];printf("请输入查找的人的姓名:->");scanf("%s", name);//判断输入信息是否存在int ret = FindByName(pc, name);if (ret == -1)printf("查找的人不存在,请重新输入\n");else{printf("%-20s\t%-10s\t%-20s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址");printf("%-20s\t%-10s\t%-20s\t%-12s\t%-30s\n", pc->data[ret].name,pc->data[ret].sex,pc->data[ret].age,pc->data[ret].tele,pc->data[ret].addr);}
}

5.修改联系人

这一步是先进行查找操作,先找到联系人,然后在这个联系人的位置重新录入一遍信息即可,代码如下:

//修改联系人
void ModifyContact(struct Contact* pc)
{char name[MAX_NAME];printf("请输入修改的人的姓名:->");scanf("%s", name);//判断输入信息是否存在int ret = FindByName(pc, name);if (ret == -1)printf("修改的人不存在,请重新输入\n");else{printf("请输入名字:>");scanf("%s", pc->data[ret].name);printf("请输入性别:>");scanf("%s", pc->data[ret].sex);printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));printf("请输入电话:>");scanf("%s", pc->data[ret].tele);printf("请输入地址:>");scanf("%s", pc->data[ret].addr);printf("修改成功\n");}
}

6.排序

我们要根据成员的某一个信息来排序,我们可以使用姓名和年龄来排序,这里我们使用qsort函数来进行相关操作:

//通过名字进行排序
int CmpByName(const void* n1, const void* n2)
{return (((struct PeoInfo*)n1)->name - ((struct PeoInfo*)n2)->name);}
//通过年龄进行排序
int CmpByAge(const void* age1, const void* age2)
{return (((struct PeoInfo*)age1)->age - ((struct PeoInfo*)age2)->age);}//排序
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByName);qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);}

7.销毁通讯录

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

//销毁
void DestroyContact(struct Contact* pc)
{free(pc->data);pc->data=NULL;pc->capacity = 0;pc->sz = 0;
}

代码以及总结

test.c代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"void menu()
{printf("**************************************\n");printf("*****   1. add      2. del       *****\n");printf("*****   3. search   4. modify    *****\n");printf("*****   5. show     6. sort      *****\n");printf("*****   0. exit                  *****\n");printf("**************************************\n");
}enum option
{ADD,DEL,SEARCH,MODIFY,SHOW,SORT,EXIT
};int main()
{int input = 0;//建立通讯录struct Contact per;//初始化通讯录InitContact(&per);do{menu();printf("请选择数字->");scanf("%d", &input);switch (input){case 1://cese ADD:AddContact(&per);break;case 2://cese DEL:DelContact(&per);break;case 3://cese SEARCH:SearchContact(&per);break;case 4://cese MODIFY:ModifyContact(&per);break;case 5://cese SHOW:ShowContact(&per);break;case 6://cese SORT:SortContact(&per);break;case 0://cese EXIT:DestroyContact(&per);break;default:printf("选择错误,请重新输入\n");break;}} while (input);return 0;
}

contact.c代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"//初始化通讯录
//void InitContact(struct Contact* pc)
//{
//	assert(pc);
//	pc->sz =0
//	memset(pc->data, 0, MAX * sizeof(struct  PeoInfo));
//
//}
//动态的初始化版本
void InitContact(struct Contact* pc)
{assert(pc);pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ *sizeof(struct PeoInfo));if (pc->data == NULL){printf("InitContact fail");return;}pc->sz = 0;pc->capacity = DEFAULT_SZ;}
//静态增加联系人
//void AddContact(struct Contact* pc)
//{
//	assert(pc);
//
//	if (pc->sz == MAX)
//	{
//		printf("通讯录已满,无法添加数据\n");
//		return;
//	}
//
//	//增加人的信息
//	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");
//}int  check_capacity(struct Contact* pc)
{if (pc->sz == pc->capacity){struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(struct PeoInfo));if (ptr != NULL){pc->data = ptr;pc->capacity += INC_SZ;printf("成功增容");return 1;}else{printf("AddContact fail");return 0;}}elsereturn 1;
}
//动态增长联系人
void AddContact(struct Contact* pc)
{assert(pc);if (0 == check_capacity(pc)){return;}//增加人的信息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 DestroyContact(struct Contact* pc)
{free(pc->data);pc->data=NULL;pc->capacity = 0;pc->sz = 0;
}//打印通讯录
void ShowContact(const struct Contact* pc)
{printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址");for (int i = 0; i < pc->sz; i++){printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].sex,pc->data[i].age,pc->data[i].tele,pc->data[i].addr);}
}static int FindByName(const struct Contact* pc, char name[])
{for(int i = 0; i < pc->sz; i++){if (0 == strcmp(pc->data[i].name, name)){return i;}}return -1;
}//删除指定联系人
void DelContact(struct Contact* pc)
{char name[MAX_NAME];printf("请输入删除的人的姓名:->");scanf("%s", name);//判断输入是否合理int ret=FindByName(pc, name);if (ret == -1)printf("删除的人不存在,请重新输入\n");else{int j = 0;for (j = ret; j < pc->sz - 1; j++){pc->data[j] = pc->data[j + 1];}pc->sz--;printf("删除成功\n");}
}//查找指定联系人
void SearchContact(const struct Contact* pc)
{char name[MAX_NAME];printf("请输入查找的人的姓名:->");scanf("%s", name);//判断输入信息是否存在int ret = FindByName(pc, name);if (ret == -1)printf("查找的人不存在,请重新输入\n");else{printf("%-20s\t%-10s\t%-20s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址");printf("%-20s\t%-10s\t%-20s\t%-12s\t%-30s\n", pc->data[ret].name,pc->data[ret].sex,pc->data[ret].age,pc->data[ret].tele,pc->data[ret].addr);}
}//修改联系人
void ModifyContact(struct Contact* pc)
{char name[MAX_NAME];printf("请输入修改的人的姓名:->");scanf("%s", name);//判断输入信息是否存在int ret = FindByName(pc, name);if (ret == -1)printf("修改的人不存在,请重新输入\n");else{printf("请输入名字:>");scanf("%s", pc->data[ret].name);printf("请输入性别:>");scanf("%s", pc->data[ret].sex);printf("请输入年龄:>");scanf("%d", &(pc->data[ret].age));printf("请输入电话:>");scanf("%s", pc->data[ret].tele);printf("请输入地址:>");scanf("%s", pc->data[ret].addr);printf("修改成功\n");}
}//通过名字进行排序
int CmpByName(const void* n1, const void* n2)
{return (((struct PeoInfo*)n1)->name - ((struct PeoInfo*)n2)->name);}
//通过年龄进行排序
int CmpByAge(const void* age1, const void* age2)
{return (((struct PeoInfo*)age1)->age - ((struct PeoInfo*)age2)->age);}//排序
void SortContact(struct Contact* pc)
{//qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByName);qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);}

contact.h代码

#pragma once
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>#define MAX_NAME 20
#define MAX_SEX  10
#define MAX_TELE 12
#define MAX_ADDR 30
#define MAX 100#define DEFAULT_SZ 3
#define INC_SZ 2struct PeoInfo
{char name[MAX_NAME];char sex[MAX_SEX];char tele[MAX_TELE];int age;char addr[MAX_ADDR];
};//静态增长版本
//struct Contact
//{
//	struct PeoInfo data[MAX];
//	int sz;
//};//动态增长版本
struct Contact
{struct PeoInfo* data;//int sz;              //int capacity;        //当前的容量
};//初始化通讯录
void InitContact(struct Contact* pc);//增加联系人
void AddContact(struct Contact* pc);//显示通讯录的信息
void ShowContact(const struct Contact* pc);//删除联系人
void DelContact(struct Contact* pc);//查找联系人
void SearchContact(const struct Contact* pc);//修改联系人
void ModifyContact(struct Contact* pc);//排序
void SortContact(struct Contact* pc);//销毁
void DestroyContact(struct Contact* pc);

总结:
以上就是关于通讯录的全部知识与介绍,大家最重要的是把整体思路捋清楚,有个大概的方向,然后慢慢去实现。


http://www.ppmy.cn/news/11508.html

相关文章

Python学习笔记——列表

Python列表函数&方法函数&#xff1a;len(list)&#xff1a;列表元素个数max(list)&#xff1a;返回列表元素最大值min(list)&#xff1a;返回列表元素最小值list(seq)&#xff1a;将元组转换为列表方法list.append(obj)&#xff1a;在列表末尾添加新的对象list.count(obj)…

软件测试复习02:静态测试

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录评审评审过程角色和职责评审类型静态分析控制流分析数据流分析编码标准一致性检查桌面检查代码走查…

Redis(三)数据类型hash

一、简介1.Redis hash是一个键值对集合2.Redis hash 是一个string类型的filed 和 value 的映射表&#xff0c;hash特别适合用于存储对象。类似java里面的Map<String,Object>。二、常用命令hset <key> <field> <value> 给key集合中的<field>键赋…

【手写 Vue2.x 源码】第十八篇 - 根据 render 函数,生成 vnode

一&#xff0c;前言 上篇&#xff0c;介绍了render 函数的生成&#xff0c;主要涉及以下两点&#xff1a; 使用 with 对生成的 code 进行一次包装将包装后的完整 code 字符串&#xff0c;通过 new Function 输出为 render 函数 本篇&#xff0c;根据 render 函数&#xff0c…

Leetcode:98. 验证二叉搜索树(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 迭代&#xff08;中序&#xff09;&#xff1a; 思路原理&#xff1a; 问题描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。…

Trime同文输入法JNI加载过程

Trime同文输入法JNI加载过程JNI初始化顺序第一步、加载librime_jni.so库第二步、自动注册机制第三步、正式加载librime_jni.so库插入一个话题、简化打印记录第四步、执行Rime.java中的init()方法LoadModules()LoadModule()rime_core_initialize()调用顺序Class不是class关键字&…

一网打尽链表的经典OJ题!链表必考笔试题第二弹

目录 0.前言 1.合并两个排序链表 1.1 实用小妙招 1.2代码书写 2.链表分割 3.链表的回文结构 4.相交链表 4.1 实用小妙招&#xff08;假定指针法&#xff09; 4.2代码书写 5. 复制带随机指针的链表 0.前言 本文代码及分析图片资源都以上传Gitee&#xff0c;可自取&a…

SpringBoot+Vue项目在线拍卖系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…