当前位置: 首页 > news >正文

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

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

    🍔静态通讯录的创建

    🍕就像是我们之前进行的完整程序逻辑的书写一样我们同样创建三个文件,两个 .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;
}

相关文章:

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

&#x1f355;在本期的博客我们来向大家介绍一下静态通讯录的书写以及怎样将我们的静态通讯录更改为动态的模式。 &#x1f354;静态通讯录的创建 &#x1f355;就像是我们之前进行的完整程序逻辑的书写一样我们同样创建三个文件&#xff0c;两个 .c 文件&#xff0c;一个 .h 文…...

2023最新最详细【接口测试总结】

序章 ​ 说起接口测试&#xff0c;网上有很多例子&#xff0c;但是当初做为新手的我来说&#xff0c;看了不不知道他们说的什么&#xff0c;觉得接口测试&#xff0c;好高大上。认为学会了接口测试就能屌丝逆袭&#xff0c;走上人生巅峰&#xff0c;迎娶白富美。因此学了点开发…...

【java基础】Stream流的各种操作

文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法flatMap方法limit和skip方法distinct方法sorted方法收集结果收集为数组&#xff08;toArray&#xff09;收集为集合&#xff08;collect&#xff09;收集为Map关于流的一些说明&#xff08;终结操…...

【Python练习】序列结构

目录 一、实验目标 二、实验内容...

CDN加速缓存的定义与作用

一、CDN的含义CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。CDN是在原有互联网的基础上再构建虚拟分发网络&#xff0c;利用部署在各地的边缘节点服务器&#xff0c;充分发挥其负载均衡、内容分发智能调度等功能&#xff0c;让用户能够就地拉取数据&#x…...

Java并发高频面试题

分享50道Java并发高频面试题。 线程池 线程池&#xff1a;一个管理线程的池子。 为什么平时都是使用线程池创建线程&#xff0c;直接new一个线程不好吗&#xff1f; 嗯&#xff0c;手动创建线程有两个缺点 不受控风险频繁创建开销大 为什么不受控&#xff1f; 系统资源有…...

CVPR 2023 | 旷视研究院入选论文亮点解读

近日&#xff0c;CVPR 2023 论文接收结果出炉。近年来&#xff0c;CVPR 的投稿数量持续增加&#xff0c;今年收到有效投稿 9155 篇&#xff0c;和 CVPR 2022 相比增加 12%&#xff0c;创历史新高。最终&#xff0c;大会收录论文 2360 篇&#xff0c;接收率为 25.78 %。本次&…...

Vue3 学习总结补充(一)

文章目录1、Vue3中为什么修改变量的值后&#xff0c;视图不更新&#xff1f;2、使用 ref 还是 reactive&#xff1f;3、reactive 为什么会有响应性连接丢失情况&#xff1f;4、watch的不同使用方法5、watchEffect和 watch 的区别区别1&#xff1a;数据源的区别区别2&#xff1a…...

使用ChatGPT 开放的 API 接口可以开发哪些自研工具?

使用ChatGPT开放的API接口,可以开发多种自研工具,例如: 智能聊天机器人:可以使用ChatGPT提供的语言生成能力,构建一个智能聊天机器人,能够根据用户的输入自动回复,完成自然语言交互。 文本生成工具:可以使用ChatGPT的文本生成能力,开发一个文本生成工具,例如自动生…...

I2C和SPI总线以及通信

通讯属性 概括 Serial/parallel 串行/并行Synchronous/asynchronous 同步/异步Point-to-point / bus 点对点 总线Half-duplex/full-duplex 半双工/全双工Master-slave/ equal partners 主从/对等single-ending / differential 单端/差分 点对点和总线 点对点通讯 只有两个通…...

Spring八股文

Bean的生命周期 1.通过反射生成对象 2.填充Bean的属性 3.调用aware接口的invokeAwareMethod方法&#xff0c;对BeanName、BeanFactory、BeanClassLoader对象的属性设值 4.调用BeanPostProcessor的前置处理方法&#xff0c;其中使用较多的是ApplicationContextPostProcessor…...

20 k8sMetric 简介

一. Metric 简介metrics-server 可实现 Kubernetes 的 Resource Metrics API&#xff08;metrics.k8s.io&#xff09;&#xff0c;通过此 API 可以查询 Pod 与 Node 的部分监控指标&#xff0c;Pod 的监控指标用于 HPA、VPA 与 kubectl top pods -n ns 命令&#xff0c;而 Node…...

面试问了解Linux内存管理吗?10张图给你安排的明明白白

linux内存管理&#xff0c;内存管理好像离我们很远&#xff0c;但这个知识点虽然冷门&#xff08;估计很多人学完根本就没机会用上&#xff09;但绝对是基础中的基础&#xff0c;这就像武侠中的内功修炼&#xff0c;学完之后看不到立竿见影的效果&#xff0c;但对你日后的开发工…...

【C++】内联函数inline

文章目录概念使用特性原理概念 C中内联函数的出现解决了C语言宏函数的不足&#xff0c;类似于宏展开&#xff0c;这种在函数调用处直接嵌入函数体的函数称为内联函数&#xff0c;又称内嵌函数或内置函数。 以inline修饰的函数叫做内联函数&#xff0c;编译时C编译器会在调用内…...

C++演讲比赛流程管理系统_黑马

任务 学校演讲比赛&#xff0c;12人&#xff0c;两轮&#xff0c;第一轮淘汰赛&#xff0c;第二轮决赛 选手编号 [ 10001 - 10012 ] 分组比赛 每组6人 10个评委 去除最高分 最低分&#xff0c;求平均分 为该轮成绩 每组淘汰后三名&#xff0c;前三名晋级决赛 决赛 前三名胜出 …...

谈谈低代码的安全问题,一文全给你解决喽

低代码是一种软件开发方法&#xff0c;通过使用图形化用户界面和可视化建模工具&#xff0c;以及自动生成代码的技术&#xff0c;使得开发人员可以更快速地构建和发布应用程序。 作为近些年软件开发市场热门之一&#xff0c;市面上也涌现了许多低代码产品&#xff0c;诸如简道云…...

[数据结构]二叉树OJ(leetcode)

目录 二叉树OJ(leetcode)训练习题&#xff1a;&#xff1a; 1.单值二叉树 2.检查两棵树是否相同 3.二叉树的前序遍历 4.另一棵树的子树 5.二叉树的构建及遍历 6.二叉树的销毁 7.判断二叉树是否是完全二叉树 二叉树OJ(leetcode)训练习题&#xff1a;&#xff1a; 1.单值二叉…...

flutter 输入时插入分隔符

每四位插入一个分隔符import package:flutter/services.dart;class DividerInputFormatter extends TextInputFormatter {final int rear; //第一个分割位数,后面分割位,,数final String pattern; //分割符DividerInputFormatter({this.rear 4, this.pattern });overrideTex…...

静态版通讯录——“C”

各位CSDN的uu你们好呀&#xff0c;之前小雅兰学过了一些结构体、枚举、联合的知识&#xff0c;现在&#xff0c;小雅兰把这些知识实践一下&#xff0c;那么&#xff0c;就让我们进入通讯录的世界吧 实现一个通讯录&#xff1a; 可以存放100个人的信息每个人的信息&#xff1a;名…...

前端基础开发环境搭建工具等

一、基本开发环境&#xff08;软件&#xff09;安装1、Vscode&#xff08;代码编辑器&#xff09;官网下载网址&#xff1a;https://code.visualstudio.com/2、nvm&#xff08;node多版本管理器&#xff0c;每个node版本都有对应的npm版本&#xff09;安装包下载地址&#xff1…...

华为OD机试题【IPv4 地址转换成整数】用 Java 解 | 含解题说明

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典本篇题目:IPv4 地址转换成整数 题目 存在…...

[数据结构]排序算法

目录 常用排序算法的实现&#xff1a;&#xff1a; 1.排序的概念及其运用 2.插入排序 3.希尔排序 4.选择排序 5.冒泡排序 6.堆排序 7.快速排序 8.归并排序 9.排序算法复杂度及稳定性分析 10.排序选择题练习 常用排序算法的实现&#xff1a;&#xff1a; 1.排序的概念及其运用…...

不愧是2023年就业最难的一年,还好有车企顶着~

就业龙卷风已经来临&#xff0c;以前都说找不到好的工作就去送外卖&#xff0c;但如今外卖骑手行业都已经接近饱和状态了&#xff0c;而且骑手们的学历也不低&#xff0c;本科学历都快达到了30%了&#xff0c;今年可以说是最难找到工作的一年。 像Android 开发行业原本就属于在…...

C/C++之while(do-while)详细讲解

目录 while循环有两个重要组成部分&#xff1a; while 是一个预测试循环 无限循环 do-while 循环 while循环有两个重要组成部分&#xff1a; 进行 true 值或 false 值判断的表达式&#xff1b;只要表达式为 true 就重复执行的语句或块&#xff1b;图 1 显示了 while 循环的…...

SpringCloud学习笔记(一)认识微服务

一、微服务技术栈 二、单体架构和分布式架构的区别 1、单体架构&#xff1a; 将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包进行部署 优点&#xff1a;架构简单&#xff0c;部署成本低缺点&#xff1a;耦合度高 2、分布式架构&#xff1a; 根据业务功能对系统…...

Unity中使用WebSocket (ws://)的方法

WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&#xff0c;两者之间就直接可以创建持久性的连接&#xff0c;并进行双向数据传输。 WebSocket与http 其…...

米哈游春招算法岗-2023.03.19-第一题-交换字符-简单题

交换字符Problem Description 米小游拿到了一个仅由小写字母组成的字符串&#xff0c;她准备进行恰好一次操作&#xff1a;交换两个相邻字母&#xff0c;在操作结束后使得字符串的字典序尽可能大。 请你输出最终生成的字符串。 input 一个仅由小写字母组成的字符串&#xff0c;…...

能把爬虫讲的这么透彻的,没有20年功夫还真不行【0基础也能看懂】

前言 可以说很多人学编程&#xff0c;不玩点爬虫确实少了很多意思&#xff0c;不管是业余、接私活还是职业爬虫&#xff0c;爬虫世界确实挺精彩的。 今天来给大家浅谈一下爬虫&#xff0c;目的是让准备学爬虫或者刚开始起步的小伙伴们&#xff0c;对爬虫有一个更深更全的认知…...

springcloud学习总结

springcloud 构建微服务项目步骤 导入依赖编写配置文件开启这个功能 Enablexxx配置类 于2023年2月24日下午17点38分开始学习于2023年3月17日晚上20点26分学完总结代码地址&#xff1a;https://gitee.com/liang-weihao/StudySpringcloud学习笔记地址&#xff1a;https://www.…...

2022年亏损超10亿,告别野蛮成长的众安在线急需新“引擎”

2023年3月21日&#xff0c;众安在线披露了2022年财报&#xff0c;营收233.52亿元&#xff0c;同比增长6.44%&#xff1b;净亏损16.33亿元&#xff0c;去年同期净利润为11.6亿元&#xff0c;同比由盈转亏。 尽管众安在线再次身陷亏损的泥潭&#xff0c;但投资者却没有选择逃离。…...

web网站开发自学指南/外链图片

一、基础知识 然后编译设备树&#xff0c; make dtbs sudo cp arch/arm/boot/dts/imx6ull-alientek-nand.dtb /home/zys/linux/tftpboot/ -f 然后以新的设备树启动linux 查看/sys/bus/i2c/devices下 会有地址为1e的设备&#xff0c;就是我们刚刚添加的设备 二、驱动编写 #i…...

网站首页关键字方案/百度seo引流怎么做

一&#xff0e;创建一张HTML页面 初学者创建一张html页面建议借助工具&#xff0c;例如Dreamweaver可视化编辑器。 二&#xff0e;<Script>标签解析 <script>xxx</script>这组标签&#xff0c;是用于在html页面中插入js的主要方法。它主要有以下几个属性&…...

企业的网站建设文章/关键词挖掘工具免费

高考考生即将迎来填报志愿环节&#xff0c;也是教育骗局最猖獗之时。个人信息泄露&#xff0c;早就不是新鲜事。但值得关注的是&#xff0c;随着移动互联技术的快速发展&#xff0c;信息泄露已呈全方位态势。 日前&#xff0c;广东省教育厅发布《广东省普通高等学校一览表》&am…...

深圳市住房和建设局网站变更/谷歌竞价广告

Django Web应用程序&#xff08;3&#xff09; 本文主要内容为对项目“学习笔记”设置样式并对其进行部署。 为设置样式&#xff0c;将使用Bootstrap库&#xff1b;另外&#xff0c;我们还将把项目部署到Heroku&#xff0c;这个网站能够让我们能够将项目推送到其服务器&#x…...

做的网站访问速度慢/小程序模板

2020第一天&#xff0c;装完了电脑&#xff0c;现在电脑能亮了&#xff0c;但是没有系统。其实接下来的步骤通过百度就可以按步就班完成 . 直接百度搜索win10软件下载&#xff0c;打开windows10官网就行&#xff0c;在这里顺便贴上地址 https://www.microsoft.com/zh-cn/softwa…...

贵阳白云区疫情最新情况今天/宁波seo网站推广

二叉树结构的实现1.二叉树相关操作2.二叉树的存储结构2.1. 二叉树的顺序存储结构2.2. 二叉链表存储结构2.3 三叉链表存储结构3.二叉树的遍历的操作3.1. 递归遍历二叉树3.2. 非递归遍历二叉树3.2.1. 基于任务分析的二叉树遍历非递归算法3.2.2. 基于搜索路径分析的二叉树遍历的非…...