> 文章列表 > 静态通讯录动态通讯录制作详解

静态通讯录动态通讯录制作详解

静态通讯录动态通讯录制作详解

    🍕在本期的博客我们来向大家介绍一下静态通讯录的书写以及怎样将我们的静态通讯录更改为动态的模式。

    🍔静态通讯录的创建

    🍕就像是我们之前进行的完整程序逻辑的书写一样我们同样创建三个文件,两个 .c 文件,一个 .h 文件。之后依次来进行详细逻辑的书写。我们依旧先来梳理一下我们静态通讯录程序的书写逻辑:

    🍕首先我们的通讯录是一个多元素的集合体:其中包括联系人姓名,联系人性别,联系人电话等信息。所以我们就需要定义一个结构题变量进而构建我们的结构体类型的集合。同时根据我们程序的需求来进行特定的操作。比如联系人的新增,删除,显示,以及按照姓名查找特定的联系人等操作。

    🍕我们通讯录的设计在主函数的test部分是和我们之前的程序的编写是相同的。都是利用do_while循环至少进行一次操作的实现之后在进行下一步操作的选择。我们通讯录的主体部分的编写依旧是我们的switch选择语句。这一部分比较简单,我们本次博客不再进行过多的讲解。

    其代码如下:

#include"contact.h"int main()
{int input = 0;do{meun();printf("请选择:");scanf("%d", &input);switch (input){case 1:break;case2:break;case 3:break;case4:break;case 5:break;case 6:break;case 0:break;default:break;}} while (input);return 0;
}

    🍕其大体结构就像是我们上面显示的那样,我们把程序的主体部分空出来以便于之后在相应的部分当中加入我们对应的功能。(和我们之前编写的函数相同 ,我们的主要的大部分函数在contact.c中进行实现,在我们的contact.h中进行声明,包括我们的头文件的声明)。

    🍕之后开始我们通讯录的正式的编写。我们在主函数中定义一个结构体,在其中声明通讯录单个对象的数据类型。就像我们正常情况下的认知一样。一般的联系人的信息有姓名,年龄,电话,性别等信息。在这里为了简便我们变量的定义,所以我们此次只定义四个变量。如果自己在编写程序的时候可以自行添加。创建之后的效果如下:

//结构体的声明
typedef struct PeoInfo
{char name[20];char sex[5];int age;char phone[12];
}PeoInfo;

    🍕当然,在我们的通讯录当中不可能只有一个联系人,因此我们还需要定义一个结构体的数组data用来存放我们不同的联系人的信息。同时值得我们注意的是,在这个数组中元素的打印个数应该怎么控制呢?我们总不可能每一次都将我们空的联系人列表也打印出来吧?所以在这里我们重新定义一个新的整形变量sz,用来管理每次通讯录打印联系人的个数。为了便于我们进行程序的编写,我们创建一个新的结构体变量来管理这两者数据。这两个数据是我们在之后程序的编写当中使用最多的部分。代码如下:

//结构体的信息
typedef struct contact
{PeoInfo data[100];int sz;
}contact;

    🍕下一个步骤就来到了我们对于通讯录的初始化了,相信有一定的计算机基础的伙伴们都应该知道当我们对数据只进行定义却不进行初始化的时候数据中放置的内容是一个随机值,因此为了避免一些不必要的麻烦,我们需要先对定义好的结构体数组和我们的整形sz元素进行初始化。我们凭借函数封装的思想我们同样可以将我们的初始化操作封装成一个函数。注意为了保证我们数组中的全部的数据都是0,在这里我们可以使用我们之前学习过的memset函数进行单个字节逐个初始化。初始化函数代码如下:

void Initcontact(contact* pa)
{pa->sz = 0;memset(pa->data, 0, sizeof(pa->data));
}

     🍕在我们初始化好结构体变量之后我们我们就可以进行我们主要函数部分的完善了。首先我们来完善第一部分添加联系人的add函数。

    🍔添加联系人函数——add函数

    🍕首先我们应该想到的就是我们可能会出现的那些特殊情况了,就比如当我们的通讯录满的时候我们就无法再继续向我们的通讯录中添加数据了。因此我们需要在实现我们添加联系人的操作之前,先进行一次判断,判断这个通讯录是否为满,如果为满的话我们就直接退出,不需要再进行下一步的操作。通讯录不满的时候我们在进行相关数据的添加。我们创建的函数因为需要对于结构体进行相应的改写,因此需要将我们函数的形参和实参类型都定义成结构体指针的类型。例如:   add(&con)。其中con为我们在主函数中创建的结构体的变量名。之后我们就可以利用指针继续相应的数据的改写操作。我们需要通过形参中的指针变量找到我们的结构体数组,再利用下标和点操作符进行相应数据的改写。其代码如下:

void add(contact* pa)
{if (pa->sz == 100){printf("通讯录已满,无法添加。\\n");return;}//添加一个人的信息printf("请输入名字:");scanf("%s", pa->data[pa->sz].name);printf("请输入电话:");scanf("%s", pa->data[pa->sz].phone);printf("请输入性别:");scanf("%s", pa->data[pa->sz].sex);printf("请输入年龄:");scanf("%d", &(pa->data[pa->sz].age));pa->sz++;
}

    🍕当我们完成上述步骤的时候,我们的添加联系人的操作也就完成了。但是我们只是添加了联系人却并不能检测我们程序编写是否存在细小的错误,所以我们下一个步骤就是编写我们的show函数将我们添加之后的联系人的信息打印出来,便于我们检测程序的正确性。

    🍔联系人显示函数——show函数

    🍕 和我们add函数编写的原理大致相同,在这里我们为了和之前的函数参数的形式相照应,因此在这里我们我们同样向函数中传入结构体指针作为函数参数。在这一部分我们使用指针找到我们的结构体的数组在进行相应数据的打印。具体的代码如下:

void show(contact* pa)
{int i = 0;for (i = 0; i < pa->sz; i++){printf("%s\\t%s\\t%d\\t%s\\n", pa->data[i].name,pa->data[i].sex,pa->data[i].age,pa->data[i].phone);}
}

    🍕当我们的show函数编写完成之后就可以进行程序效果的检查了。只需要在我们主函数中的switch语句中填入相应的函数即可,程序运行的效果如下:

    🍕我们先选择1添加一系列的数据向我们的通讯录当中,之后选择5调用我们show函数,显示我们的通讯录的主要的数据对象。

    🍔联系人的查找——find函数

    🍕之后我们来完成我们的下一个函数。根据分析我们可以知道:当我们在调用del函数的时候我们需要先找到我们想要删除的联系人。如果我们想要修改我们的联系人的信息的时候我们同样需要先找到我们指定联系人的信息,所以我们只需要完成我们的find函数的时候,之后我们其他相应函数的编写只需要复用find函数的内容即可。find函数的编写其实也很简单,我们只需要遍历我们的结构体数组中的数据即可,只要我们查找的数据项相同的时候我们返回当前的下标,如果没有找到,就返回-1。便于我们后面的函数的使用。find函数的具体实现代码如下:

int find(contact* pa)
{if (pa->sz == 0){printf("通讯录为空,无法查找。\\n");return -1;}int i = 0;char name[20];printf("请输入你想要查找的对象的名字:");scanf("%s", name);for (i = 0; i < pa->sz; i++){if (strcmp(pa->data[i].name, name) == 0){printf("%-5s\\t%-5s\\t%-5s\\t%-13s\\n","姓名","性别","年龄","电话");printf("%-5s\\t%-5s\\t%-5d\\t%-13s\\n", pa->data[i].name, pa->data[i].sex, pa->data[i].age, pa->data[i].phone);return i;}}printf("没有找到相应的联系人。\\n");return -1;
}

    🍕在这里我们需要注意的同样是对于我们通讯录逻辑的构建,假如我们的结构体数组中的元素个数为0时,就无需检查元素对象,直接提示并退出即可,如果没有找到也进行提示并退出。那么我们find函数的主要内容也就实现完毕了。

    🍔联系人删除函数——del函数

   🍕接着编写我们的del函数,我们同样需要先判断通讯录是否为空,如果为空,直接提示并返回,之后我们服用我们find函数当中的代码,就可以直接查找到我们相应的元素,如果函数的返回值不为-1的时候就代表找到了相应的数据元素,之后我们就可以进行下一步的删除操作。如果返回值为-1,那么就代表没有找到我们数据的元素,所以我们需要提示并退出。在我们del函数的具体实现当中,我们只需要将元素一个一个从后向前移动即可。(使用后面的有效的数据遮盖住我们前面不需要使用的数据)最后对sz进行 -- 操作即可。值得我们在这一部分特别注意的是:我们要格外小心数组越界的问题,因此我们要将我们循环的结束条件 -1 以防止数组越界。这一部分的代码实现如下:

void del(contact* pa)
{if (pa->sz == 0){printf("通讯录为空,不能进行删除操作。");return;}char name[20];printf("请输入你想要删除联系人的名字:");scanf("%s", name);int ret = 0;int i = 0;for (i = 0; i < pa->sz; i++){if (strcmp(pa->data[i].name, name) == 0){printf("%-5s\\t%-5s\\t%-5s\\t%-13s\\n", "姓名", "性别", "年龄", "电话");printf("%-5s\\t%-5s\\t%-5d\\t%-13s\\n", pa->data[i].name, pa->data[i].sex, pa->data[i].age, pa->data[i].phone);ret = i;}}if (ret !=-1 ){//查找到相关的联系人并删除int i = 0;for (i = ret; i < pa->sz - 1; i++){pa->data[i] = pa->data[i + 1];}pa->sz--;printf("删除成功。\\n");return;}else{printf("查无此人,请重新确认。\\n");return;}
}

     🍕让程序运行起来,之后我们代码的运行效果就是如下:

    🍕可以看出我们程序运行的很顺利,最后一部分的功能就是我们的modify函数了。

    🍔联系人修改函数——modify函数

    🍕我们现在来完成我们程序的最后一部分:modify函数的实现。想要修改一个联系人我们第一步要做的就是查找到我们的联系人的信息。在这里我们同样复用我们的find函数的代码即可。之后对于新的修改的内容,我们可以利用我们find函数的返回值进行指定数据的改写。(返回值就是我们想要修改的数据的下标)直接进行复制改写即可。其实现的代码如下:

void modify(contact* pa)
{if (pa->sz == 0){printf("通讯录列表为空,无法进行修改。\\n");return;}char name[20];printf("请输入你想要修改的联系人的姓名:");scanf("%s", name);int ret = 0;int i = 0;for (i = 0; i < pa->sz; i++){if (strcmp(pa->data[i].name, name) == 0){printf("%-5s\\t%-5s\\t%-5s\\t%-13s\\n", "姓名", "性别", "年龄", "电话");printf("%-5s\\t%-5s\\t%-5d\\t%-13s\\n", pa->data[i].name, pa->data[i].sex, pa->data[i].age, pa->data[i].phone);ret = i;}}if (ret != -1){//进行相关的修改操作printf("请输入新的名字:");scanf("%s", pa->data[ret].name);printf("请输入新的电话:");scanf("%s", pa->data[ret].phone);printf("请输入新的性别:");scanf("%s", pa->data[ret].sex);printf("请输入新的年龄:");scanf("%d", &(pa->data[ret].age));printf("修改成功。\\n");return;}else{printf("查无此人,请重新确认。\\n");return;}
}

    🍕我们让我们的程序运行起来进行检验,运行效果如下:

    🍕可以发现一切正常,那么到此为止,我们的静态通讯录的程序的编写也就完全结束了。下面我们将静态通讯录改写成动态通讯录。

    🍔动态通讯录 

    🍕 静态通讯录和动态通讯录的最大的差别就是静态通讯录只能再我们创建结构体数组的时候创建,一旦数据数量满的时候我们就不能再向其中输入数据。但是我们动态通讯录的编写就不需要考虑这个问题,动态通讯录会自动适应我们的数据的数量,当我们的位置不够的时候会自动开辟我们新的空间。让我们的通讯录变得更加的灵活。在这里我们使用calloc函数和realloc函数实现我们的动态的通讯录。

    🍕首先来梳理一下我们动态通讯录中需要完善的程序的功能,我们需要使用calloc函数开辟一个新的堆区的动态空间,之后再使用realloc函数调整增加我们的函数。

    🍕第一步我们需要调整我们的结构体的定义的内容。还记得我们最初对于数据数组的存储是使用一个定态数组,我们在这里将我们的定长数组修改为一个指针便于接收我们后面传入的动态开辟好的指针。

    🍕之后我们还需要修改初始化我们通讯录的函数。在这一部分我们需要修改的内容较多。首先我们再这一部分需要使用calloc函数开辟一个动态空间,(这里使用calloc函数不适用malloc函数的原因是calloc函数会自动初始化为0,便于节省我们的步骤。)在我们开辟好空间之后可以先创立一个指针进行接收,判断不为空指针之后在进行对结构体指针进行赋值。最后再将我们的sz的数值初始化为0。

    🍕在这里我们需要考虑的是我们在使用动态开辟的空间的时候肯定需要考虑并避免越界的问题,所以我们需要新创建一个capicity变量用于记录我们当前通讯录的容量,如果通讯录的容量使用完毕之后需要考虑我们的增容的问题。我们创建初始化通讯录的代码如下:

//动态通讯录
void Initcontact(contact* pa)
{//初始化通讯录PeoInfo*ptr =(PeoInfo*)calloc(3, sizeof(PeoInfo));if (ptr != NULL){pa->data = ptr;}pa->sz = 0;pa->capicity = 3;
}

    🍕之后我们需要在我们之前创建好的add函数中假如我们的整容的判断,如果当我们的容量和我们的数据的数量相等的时候就进行增容。

    🍕我们可以重新定义一个check_capicity函数,并在适当的时候进行通讯录的扩容(使用realloc重新调整空间的大小,如果我们容量增加之后就可以给用户提示:增容成功。)增容部分的代码如下:

//检查增容的函数
void check_capicity(contact* pa)
{if (pa->sz < pa->capicity){return;}else{//通讯录已满,需要增容PeoInfo* ptr = realloc(pa->data, (pa->capicity + 2) * sizeof(PeoInfo));if (ptr != NULL){pa->data == ptr;pa->capicity = pa->capicity + 2;printf("增容成功。\\n");return;}else{printf("增容失败。\\n");return;}}
}//动态通讯录增加函数
void add(contact* pa)
{//检查是否需要增容check_capicity(pa);//添加一个人的信息printf("请输入名字:");scanf("%s", pa->data[pa->sz].name);printf("请输入电话:");scanf("%s", pa->data[pa->sz].phone);printf("请输入性别:");scanf("%s", pa->data[pa->sz].sex);printf("请输入年龄:");scanf("%d", &(pa->data[pa->sz].age));pa->sz++;
}

    🍕当我们编写完增容函数之后会发现其他的内容都并没有改变,那么我们动态修改通讯录的最后一步就是创建一个destory函数了。因为我们向堆区动态开辟一个内存使用完之后,肯定要去释放我们开辟好的内存。否则就可能会造成内存泄漏的问题。

    🍕对于我们的destory函数的编写其实很简单,只需要将我们接收指针的变量free(释放)掉,之后就可以了。之后将我们的指针置为空指针。将我们通讯录的容量修改为0,变将我们的sz修改为0。之后我们通讯录的修改也就结束了。

    🍔文件保存

    🍕当我们的动态通讯录书写完毕之后我们就会发现,虽然一切功能看似正常,但是唯一的不足就是不能保存。所以为了让我们的代码更加的完善我们增加一个函数的保存的函数。

    🍕和我们正常的文件保存函数相同的是,我们通讯录的文件保存是打开一个文件然后以文本或者二进制的形式写入文件当中。首先我们需要使用fopen函数打开一个文件,之后在关闭文件之前就可以向文件中写入我们想要写入的数据。在这里我们以二进制的形式向文件中写入数据。(使用fwrite函数)等我们写完文件之后就可以关闭文件并将我们打开文件的接受指针置为空即可。文件保存部分的函数如下:

//保存通讯录函数
void SaveContact(contact* pc)
{//写数据//1, 打开文件FILE* pf = fopen("contact.txt", "wb");if (NULL == pf){perror("SaveContact");}else{//写数据int i = 0;for (i = 0; i < pc->sz; i++){fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);}fclose(pf);pf = NULL;printf("保存成功\\n");}
}

    🍕等我们的数据全部写入内存之后我们需要考虑的是我们只是增添了向文件中写入数据的函数,但是我们每次运行程序的时候还得从文件当中读取数据,那么我们就需要重新创建一个文件读取函数。我们只需要格外注意一点:那就是我们必须在向内存中写入数据的时候必须考虑到容量的问题,当我们的容量满的时候就需要考虑增容的情况,这个时候我们就需要调用我们上面写好的增容的函数。文件读取函数部分的代码如下:

void LoadContact(contact* pc)
{//读数据//1. 打开文件FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("LoadContact");}else{//2. 读数据PeoInfo tmp = { 0 };int i = 0;while (fread(&tmp, sizeof(PeoInfo), 1, pf)){//增容check_capicity(pc);pc->data[i] = tmp;pc->sz++;i++;}fclose(pf);pf = NULL;}
}

    🍕以上我们的通讯录的程序也就全部完成了。下面是有关通讯录的全部代码,可以自行参考。那么本次博客的内容也就到此结束了,感谢您的观看与支持。

//contact.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"void meun(void)
{printf("***********************************\\n");printf("***   1.add       2.find        ***\\n");printf("***   3.del       4.modify      ***\\n");printf("***   5.show      0.exit        ***\\n");printf("***********************************\\n");
}//静态通讯录
//void Initcontact(contact* pa)
//{
//	pa->sz = 0;
//	memset(pa->data, 0, sizeof(pa->data));
//}//动态通讯录
void Initcontact(contact* pa)
{//初始化通讯录PeoInfo*ptr =(PeoInfo*)calloc(3, sizeof(PeoInfo));if (ptr != NULL){pa->data = ptr;}pa->sz = 0;pa->capicity = 3;
}//静态通讯录增加函数
//void add(contact* pa)
//{
//	if (pa->sz == 100)
//	{
//		printf("通讯录已满,无法添加。\\n");
//		return;
//	}
//	//添加一个人的信息
//	printf("请输入名字:");
//	scanf("%s", pa->data[pa->sz].name);
//	printf("请输入电话:");
//	scanf("%s", pa->data[pa->sz].phone);
//	printf("请输入性别:");
//	scanf("%s", pa->data[pa->sz].sex);
//	printf("请输入年龄:");
//	scanf("%d", &(pa->data[pa->sz].age));
//	pa->sz++;
//}//检查增容的函数
void check_capicity(contact* pa)
{if (pa->sz < pa->capicity){return;}else{//通讯录已满,需要增容PeoInfo* ptr = realloc(pa->data, (pa->capicity + 2) * sizeof(PeoInfo));if (ptr != NULL){pa->data = ptr;pa->capicity = pa->capicity + 2;printf("增容成功。\\n");return;}else{printf("增容失败。\\n");return;}}
}//动态通讯录增加函数
void add(contact* pa)
{//检查是否需要增容check_capicity(pa);//添加一个人的信息printf("请输入名字:");scanf("%s", pa->data[pa->sz].name);printf("请输入电话:");scanf("%s", pa->data[pa->sz].phone);printf("请输入性别:");scanf("%s", pa->data[pa->sz].sex);printf("请输入年龄:");scanf("%d", &(pa->data[pa->sz].age));pa->sz++;
}void show(contact* pa)
{int i = 0;for (i = 0; i < pa->sz; i++){printf("%s\\t%s\\t%d\\t%s\\n", pa->data[i].name,pa->data[i].sex,pa->data[i].age,pa->data[i].phone);}
}int find(contact* pa)
{if (pa->sz == 0){printf("通讯录为空,无法查找。\\n");return -1;}int i = 0;char name[20];printf("请输入你想要查找的对象的名字:");scanf("%s", name);for (i = 0; i < pa->sz; i++){if (strcmp(pa->data[i].name, name) == 0){printf("%-5s\\t%-5s\\t%-5s\\t%-13s\\n","姓名","性别","年龄","电话");printf("%-5s\\t%-5s\\t%-5d\\t%-13s\\n", pa->data[i].name, pa->data[i].sex, pa->data[i].age, pa->data[i].phone);return i;}}printf("没有找到相应的联系人。\\n");return -1;
}//通讯录销毁函数
void Destory(contact* pa)
{free(pa->data);pa->data = NULL;pa->capicity = 0;pa->sz = 0;return;
}void del(contact* pa)
{if (pa->sz == 0){printf("通讯录为空,不能进行删除操作。");return;}char name[20];printf("请输入你想要删除联系人的名字:");scanf("%s", name);int ret = 0;int i = 0;for (i = 0; i < pa->sz; i++){if (strcmp(pa->data[i].name, name) == 0){printf("%-5s\\t%-5s\\t%-5s\\t%-13s\\n", "姓名", "性别", "年龄", "电话");printf("%-5s\\t%-5s\\t%-5d\\t%-13s\\n", pa->data[i].name, pa->data[i].sex, pa->data[i].age, pa->data[i].phone);ret = i;}}if (ret !=-1 ){//查找到相关的联系人并删除int i = 0;for (i = ret; i < pa->sz - 1; i++){pa->data[i] = pa->data[i + 1];}pa->sz--;printf("删除成功。\\n");return;}else{printf("查无此人,请重新确认。\\n");return;}
}void modify(contact* pa)
{if (pa->sz == 0){printf("通讯录列表为空,无法进行修改。\\n");return;}char name[20];printf("请输入你想要修改的联系人的姓名:");scanf("%s", name);int ret = 0;int i = 0;for (i = 0; i < pa->sz; i++){if (strcmp(pa->data[i].name, name) == 0){printf("%-5s\\t%-5s\\t%-5s\\t%-13s\\n", "姓名", "性别", "年龄", "电话");printf("%-5s\\t%-5s\\t%-5d\\t%-13s\\n", pa->data[i].name, pa->data[i].sex, pa->data[i].age, pa->data[i].phone);ret = i;}}if (ret != -1){//进行相关的修改操作printf("请输入新的名字:");scanf("%s", pa->data[ret].name);printf("请输入新的电话:");scanf("%s", pa->data[ret].phone);printf("请输入新的性别:");scanf("%s", pa->data[ret].sex);printf("请输入新的年龄:");scanf("%d", &(pa->data[ret].age));printf("修改成功。\\n");return;}else{printf("查无此人,请重新确认。\\n");return;}
}//保存通讯录函数
void SaveContact(contact* pc)
{//写数据//1, 打开文件FILE* pf = fopen("contact.txt", "wb");if (NULL == pf){perror("SaveContact");}else{//写数据int i = 0;for (i = 0; i < pc->sz; i++){fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);}fclose(pf);pf = NULL;printf("保存成功\\n");}
}void LoadContact(contact* pc)
{//读数据//1. 打开文件FILE* pf = fopen("contact.txt", "rb");if (pf == NULL){perror("LoadContact");}else{//2. 读数据PeoInfo tmp = { 0 };int i = 0;while (fread(&tmp, sizeof(PeoInfo), 1, pf)){//增容check_capicity(pc);pc->data[i] = tmp;pc->sz++;i++;}fclose(pf);pf = NULL;}
}//contact.h文件
#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>
#include<string.h>
#include<stdlib.h>//菜单函数
void meun(void);//结构体的声明
typedef struct PeoInfo
{char name[20];char sex[5];int age;char phone[12];
}PeoInfo;//结构体的信息
typedef struct contact
{PeoInfo* data;int sz;int capicity;
}contact;//初始化通讯录的函数
void Initcontact(contact* pa);//显示我们制作好的通讯录
void show(contact* pa);//查找联系人的函数
int find(contact* pa);//删除联系人的函数
void del(contact* pa);//修改联系人信息的函数
void modify(contact* pa);//通讯录销毁函数
void Destory(contact*pa);//退出时保存通讯录的函数
void SaveContact(contact* pc);//加载通讯录信息到通讯录
void LoadContact(contact* pc);//test.c文件
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"int main()
{//创建通讯录contact con;int input = 0;//初始化通讯录Initcontact(&con);LoadContact(&con);do{meun();printf("请选择:");scanf("%d", &input);switch (input){case 1:add(&con);break;case 2:find(&con);break;case 3:del(&con);break;case 4:modify(&con);break;case 5:show(&con);break;case 0:SaveContact(&con);printf("退出通讯录。\\n");Destory(&con);break;default:printf("输入错误请重新输入。\\n");break;}} while (input);return 0;
}

公积金领取