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

【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化

引言

动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。

        

 158c3f50b199454985017a51dbef9841.png               ✨ 猪巴戒:个人主页✨

               所属专栏:《C语言进阶》

        🎈跟着猪巴戒,一起学习C语言🎈

目录

引言

为什么存在动态内存分配

malloc

动态内存空间位置

 内存泄漏问题

free

calloc   

realloc 

realloc如何开辟动态内存空间

动态版本的通讯录

常见动态内存错误

1.对NULL空指针进行解引用

2.对动态开辟空间的越界访问

3.对非动态开辟的内存使用free释放

4.使用free释放动态开辟内存的一部分

5.对同一块动态内存多次释放

6.动态开辟内存忘记释放(内存释放)

动态通讯录的实现



为什么存在动态内存分配

int main()
{int arr[20] = {0};//整形数组开辟20个元素,就是80个字节。return 0;
}

1.空间开辟大小是固定的

2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

如果我们开辟的空间不够,那么进行修改会比较麻烦。如果开辟的空间较大,那么所占据的空间又会很大。那么有没有一种方法可以用多少空间就开辟多大的内存呢?

这个时候就有了动态内存开辟。


malloc

动态内存函数的头文件  <stdlib.h>

void* malloc (size_t size)

a066e21521c34f6e966d7c58c5b1d1fb.png

申请内存块,

size

就是我们要申请的字节大小。当然,我们的内存是有限的,不是想要申请多少内存就可以申请多少内存。

返回值

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

开创的空间放回地址是void*类型,使用的时候记得要强制类型转换。

所以我们在使用malloc函数时,要检测返回值p是否是空指针。

#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include<stdio.h>
int main()
{int arr[10] = { 0 };//动态内存开辟int* p = (int*)malloc(40);if (p == NULL)      //检测返回的p是不是空指针。{printf("%s\n", strerror(errno));return 1;}//使用动态内存int i = 0;for (i = 0; i < 10; i++){*(p + i) = i;}for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放free(p);    //常常搭配malloc、calloc、realloc使用,后面会讲到p = NULL;return 0;
}

f8d11f8a978c45c9a26e6b479d12acb3.png

         

动态内存空间位置

 动态内存函数是在堆区开辟内存空间的。我们一般的局部变量、形式参数都是存放在栈区。

2a755d6571ce4bc28cdd42725109c2d3.png

         

 内存泄漏问题

什么是内存泄漏?

我们创建的局部变量,数据会在函数结束时释放。

动态内存空间存放临时使用的数据,这些数据不必等到函数结束时释放,而是需要时随时开辟,不需要时,随时释放。动态开辟的内存使用完是要进行释放的,如果不对内存进行释放,那么开辟的动态内存就会被之前的数据占据,这部分的内存就无法使用,相当于丢失了内存。因此我们把这类问题叫做内存泄漏

下面的代码如果吧进行释放,那么就会一直占据内存空间。

858d90982a85432cbc47bdc0798a0ef1.png

 通过释放动态内存,内存可以被重新调用

9a8aa7cd08754d5db41e8a501d5ee008.png

最后我们加了一步,p = NULL,p的内存空间已经被释放了还给操作系统了,但是p还是原来的地址,我们通过p就会使用到一个已经释放的内存,这就会导致野指针问题。为了避免这种情况,我们将p的地址去掉就可以了。

17cef251579d40c797c86392201a90f2.png

        

free

void free (void* ptr)

free函数是专门用来释放动态开辟的内存。

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数ptr 是NULL指针,则函数什么事都不做。

free函数只能释放动态内存开辟的空间,如果释放其他空间,就会报错。

#include <stdlib.h>
int main()
{int a = 0;int* p = &a;free(p);p = NULL;return 0;
}

da583dd28bf24bd482fa2e8067931150.png

        

calloc   

void* calloc (size_t num, size_t size)

f79aadb251314d11839bd0058f502487.png

num 

开辟空间元素的个数

size

空间中每个元素的大小。

  •  函数的功能为num个大小为size 的元素开辟一块空间,并且把空间的每个字节初始化为0
  • callocmalloc的区别在于calloc在放回地址之前把申请的空间的每个字节初始化为全0。

下面的例子就是解释:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)calloc(10, sizeof(int));if (p == NULL){printf("%s\n", strerror(errno));}//打印int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

a523a57b3c8a4aae938562d1611e95f6.png

        

realloc 

void* realloc (void* ptr, size_t size)

2843916f62db4e2b8fceee1671efeb18.png

ptr

要调整的空间的起始位置。

size

  • realloc函数的出现让动态内存管理更加灵活。
  • 有时我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间豁达了,那为了合理的内存,我们一定对内存的大小做灵活的调整。那realloc函数可以做到对动态开辟内存大小的调整。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));}//使用int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;}//扩容int* ptr =  realloc(p, 80);if (ptr != NULL)  //这里不直接将新的地址赋给p,是因为realloc有可能开辟失败返回空指针。后面详细讲{p = ptr;}for(i=0;i<10;i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

88f1530abbf448fbb5a1f45dc2e8873f.png

        

realloc如何开辟动态内存空间

realloc在调整内存空间的是存在两种情况。

第一种情况,原空间之后有足够大的空间。当要进行调整的内存空间后面有多余的40个字节空间,那么就可以直接开辟向后面开辟40个字节的空间,然后放回起始位置的地址,这里指的是0的地址。

5de786ff5e05407c92d31f2e1c575847.png

698f0ff7ee3441a580545c584564081e.png

第二种情况原空间之后没有足够大的空间。要调整的内存空间后面不足以存放40个字节的空间,那么就要重新找到一个新的地址(可以存放80个字节)开辟,开辟成功后返回起始位置的地址。

注意:如果需要开辟的空间过大,是会开辟失败的,开辟失败,realloc返回空指针,所以要检查空指针。

这里把realloc开辟的空间换成8000,来实现第二种情况。

1ad74892b124494c959288e26152895b.png e2a52b304f504866a715c4e5bcbbefb8.pngf4bc4b17e6db40799be54b0eb0d8fc7e.png

         

动态版本的通讯录

动态:

要实现通讯录容量的动态化,要实现两个功能

1.通讯录默认能存放3个人的信息

2.如果空间不够了,就增加空间,每次增加2个人的空间

静态:

原来的通讯录的信息由结构体组成的数组,来存放信息,设置的是100人的信息。

1.当没有100人的信息时,会造成空间上的浪费。

2.当超过100人的信息时,又无法自动扩容,如果想要扩容要手动改变最大存放的空间。

原通讯录的代码:

有兴趣可以学习通讯录的实现http://t.csdnimg.cn/UbT9I

 存放数据的改变

首先把存放联系人信息的结构体给大家看看

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

原来存放联系人的信息是通过数组data[100],这样的数组的空间是固定的,是一开始设置的最大容量。

typedef struct Contact
{PeoInfo data[MAX];int count;//记录当前通讯录中实际人数的个数
}Contact;

不以数组的形式进行存放,以数组改成地址,但是地址也可以像数组一样访问数据。data是联系人的起始地址,如果想要访问第二个人,就是*(data+1),等于data[1]。

count是当前使用了多少个联系人的空间.

capacity记录的是现在存放联系人的容量是多大。因为当每次容量不够时,就增加空间,每次增加2个人的空间。也就是每次count == capacity的时候,capacity就要加2.

typedef struct Contact
{PeoInfo* data;int count;//记录当前通讯录中实际人数的个数int capacity;
}Contact;

1.初始化的改变

assert函数,用来检验空指针,如果为空指针,就会报错。

参数pc,是创立的struct Contact结构体变量的地址,这里是传址调用,作用就是改变原来的数据。

原通讯录是存放100个联系人的数组,将100个联系人的数据初始化为0。

void InitContact(Contact* pc)
{assert(pc);pc->count = 0;memset(pc->data, 0, sizeof(pc->data));
}

动态通讯录要实现开辟3个联系人的空间,并将它们进行初始化。 既要开辟空间,又要进行初始化,我们想到calloc函数。

malloc是单纯地开辟空间,realloc是既开辟空间,并进行初始化。mallocrealloc地区别就在于是否对开辟的空间初始化。

开辟3个联系人空间,calloc进行开辟,将地址传给pc->data.将记录联系人的容量传给capacity.

int InitContact(Contact* pc)
{assert(pc);pc->count = 0;pc->data =(int*) calloc(3, sizeof(PeoInfo));if (pc == NULL){printf("InitContact::%s\n", strerror(errno));return 1;}pc->capacity = 3;return 0;
}

 2.增加联系人

参数pc,是创立的struct Contact结构体变量的地址,这里是传址调用,作用就是改变原来的数据。

动态内存管理,通过pc->data[count]可以进行数据的输入。最重要的是实现通讯录容量的动态化。

count表示已经使用的联系人数量,capacity表示联系人的总容量。

当count == capacity时,就要动态开辟内存,对容量进行增容。

CheckCapacity为自定义增容函数,我们要实现增容的功能。

realloc重新开辟内存块,实现内存的动态化。realloc返回的起始地址不能直接传给data,因为动态内存的开辟有可能失败,失败传回空指针。

capacity增加2,最后提示增容成功。

void CheckCapacity(Contact* pc)
{if (pc->count == pc->capacity);{PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(PeoInfo));if (ptr == NULL){printf("AddContact::%s\n", strerror(errno));return 1;}else{pc->data = ptr;pc->capacity += 2;printf("增容成功\n");}}
}
void AddContact(Contact* pc)
{assert(pc);//增容CheckCapacity(pc);printf("请输入名字:》");scanf("%s", pc->data[pc->count].name);printf("请输入年龄:》");scanf("%d", &(pc->data[pc->count].age));printf("请输入性别:>");scanf("%s", pc->data[pc->count].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->count].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->count].addr);pc->count++;printf("增加成功\n");
}

将整体的代码呈现在文章末尾。

        

常见动态内存错误

        

1.对NULL空指针进行解引用

如果开辟的空间过大,malloc有可能开辟失败,开辟失败就会返回空指针。如果直接对p进行解引用,就会产生问题。

#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);*p = 20;return 0;
}

正确解决方法: 

在开辟动态内存后对p进行检验,是否为空指针。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}*p = 20;free(p);p = NULL;return 0;
}

        

2.对动态开辟空间的越界访问

我们只开辟了10个字节的空间,但是访问,从0到10,10算进去的话就是11个元素,这里访问越界了,就会出问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i <= 10;i++){p[i] = i;}free(p);p = NULL;return 0;
}

正确解决方法: 

一定要注意我们开辟的空间是否和访问的空间是一样的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i < 10;i++){p[i] = i;}free(p);p = NULL;return 0;
}

        

3.对非动态开辟的内存使用free释放

free只能够释放动态开辟的内存,不能够随意去使用函数。

#include <stdlib.h>
int main()
{int a = 10;int* p = &a;//....free(p);p = NULL;return 0;
}

ec2893e717184b06857487260a7a58de.png

        

4.使用free释放动态开辟内存的一部分

free做不到释放动态内存的一部分,如果要释放要将整个动态内存进行释放。

#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);if (p = NULL){return 1;}int i = 0;for (i = 0; i < 10; i++){*p = i;p++;}free(p);p = NULL;return 0;
}

正确解决方法: 

不改变p的位置,对p进行释放。使用p的话,通过:

*(p+i) = i;

        

5.对同一块动态内存多次释放

free的二次使用,第二次使用的动态内存空间已经还给操作系统了,但是还能对p进行操作,就是野指针问题。

#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);free(p);//....free(p);return 0;
}

正确解决方法: 

避免free的二次使用,或者将p转化为空指针。

#include <stdlib.h>
int main()
{int* p = (int*)malloc(40);free(p);p = NULL;free(p);return 0;
}

        

6.动态开辟内存忘记释放(内存释放)

看看下面的例子:

如果flag = 5的话,那么后面free函数就会跳过,动态开辟的内存就不能释放。

#include <stdio.h>
#include <stdlib.h>
void test()
{int* p = (int*)malloc(100);int flag = 0;scanf("%d", &flag);if (flag == 5){return;}free(p);p = NULL;
}
int main()
{test();return 0;
}

动态通讯录的实现

 contact.h

头文件:用来对函数的声明

#pragma once#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>#define DEFAULT_SZ 3
#define INC_SZ 2
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30//类型的声明
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;typedef struct Contact
{PeoInfo* data;int count;//记录当前通讯录中实际人数的个数int capacity;
}Contact;//初始化通讯录
int InitContact(Contact* pc);//销毁通讯录
void DestroyContact(Contact* pc);//增加联系人到通讯录
void AddContact(Contact* pc);//打印通讯录中信息
void ShowContact(const Contact* pc);//删除联系人的信息
void DelContact(Contact* pc);//查找指定联系人
void SeachContact(Contact* pc);//修改指定联系人
void ModifyContact(Contact* pc);//排序通讯录中的内容
//按照名字来排序
void SortContact(Contact* pc);

contact.c

源文件:函数的实现和定义

#define _CRT_SECURE_NO_WARNINGS#include "contact.h"//动态版本
int InitContact(Contact* pc)
{assert(pc);pc->count = 0;pc->data =(int*) calloc(DEFAULT_SZ, sizeof(PeoInfo));if (pc == NULL){printf("InitContact::%s\n", strerror(errno));return 1;}pc->capacity = DEFAULT_SZ;return 0;
}void DestroyContact(Contact* pc)
{assert(pc);free(pc->data);pc->data = NULL;
}void CheckCapacity(Contact* pc)
{if (pc->count == pc->capacity);{PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));if (ptr == NULL){printf("AddContact::%s\n", strerror(errno));return 1;}else{pc->data = ptr;pc->capacity += INC_SZ;printf("增容成功\n");}}
}
void AddContact(Contact* pc)
{assert(pc);//增容CheckCapacity(pc);printf("请输入名字:》");scanf("%s", pc->data[pc->count].name);printf("请输入年龄:》");scanf("%d", &(pc->data[pc->count].age));printf("请输入性别:>");scanf("%s", pc->data[pc->count].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->count].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->count].addr);pc->count++;printf("增加成功\n");
}void ShowContact(const Contact* pc)
{assert(pc);int i = 0;//一个汉字是两个字符printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->count; i++){printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,pc->data[i].age,pc->data[i].sex,pc->data[i].tele,pc->data[i].addr);}
}static int FindByName(Contact* pc, char name[])
{assert(pc);int i = 0;for (i = 0; i < pc->count; i++){if (0 == strcmp(pc->data[i].name, name)){return i;}}return -1;
}void DelContact(Contact* pc)
{char name[MAX_NAME] = { 0 };assert(pc);int i = 0;if (pc->count == 0){printf("通讯录为空,没有信息可以删除\n");return;}printf("请输入要删除人的名字:>");scanf("%s", name);//删除//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}//2.删除for (i = pos; i < pc->count; i++){pc->data[i] = pc->data[i + 1];}pc->count--;
}void SeachContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入需要查找的联系人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}//2.打印printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);}void ModifyContact(Contact* pc)
{assert(pc);char name[MAX_NAME] = { 0 };printf("请输入需要查找的联系人的名字:>");scanf("%s", name);//1.查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}printf("要修改人的信息已经找到,接下来进行修改\n");//2.修改printf("请输入名字:》");scanf("%s", pc->data[pos].name);printf("请输入年龄:》");scanf("%d", &(pc->data[pos].age));printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}int cmp_peo_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
//按照名字来排序
void SortContact(Contact* pc)
{assert(pc);qsort(pc->data, pc->count, sizeof(PeoInfo), cmp_peo_by_name);printf("排序成功\n");
}

test.c

通讯录主脉络:

#define _CRT_SECURE_NO_WARNINGS#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");}
int main()
{int input = 0;Contact con;//初始化通讯录:模块化初始化InitContact(&con);//只能传地址,进行修改do{menu();printf("请选择:》");scanf("%d", &input);switch (input){case 1:AddContact(&con);break;case 2:DelContact(&con);break;case 3:SeachContact(&con);break;case 4:ModifyContact(&con);break;case 5:ShowContact(&con);break;case 6:SortContact(&con);break;case 0:DestroyContact(&con);printf("退出通讯录\n");break;default:printf("选择错误\n");}} while (input);return 0;
}

相关文章:

【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化

引言 动态内存管理的函数有&#xff1a;malloc,calloc,ralloc,free,本文讲解动态内存函数和使用&#xff0c;如何进行动态内存管理,实现通讯录联系人容量的动态化&#xff0c;对常见动态内存错误进行总结。 ✨ 猪巴戒&#xff1a;个人主页✨ 所属专栏&#xff1a;《C语言进阶》…...

Centos9(Stream)配置Let‘s Encrypt (免费https证书)

1. 安装snap&#xff0c;用来安装certbot&#xff1a; sudo dnf install epel-release sudo dnf upgrade sudo yum install snapd sudo systemctl enable --now snapd.socket sudo ln -s /var/lib/snapd/snap /snap snap install core snap refresh core 2. 安装 certbot命令…...

Spring之事务(2)

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…...

嵌入式科普(5)ARM GNU Toolchain相关概念和逻辑

一、目的/概述 二、资料来源 三、逻辑和包含关系 四、Arm GNU Toolchain最常用的命令 嵌入式科普(5)ARM GNU Toolchain相关概念和逻辑 一、目的/概述 对比高集成度的IDE(MDK、IAR等)&#xff0c;Linux开发需要自己写Makefile等多种脚本。eclipse、Visual Studio等需要了解预处…...

Elasticsearch:什么是文本分类?

文本分类定义 - text classification 文本分类是一种机器学习&#xff0c;它将文本文档或句子分类为预定义的类或类别。 它分析文本的内容和含义&#xff0c;然后使用文本标签为其分配最合适的标签。 文本分类的实际应用包括情绪分析&#xff08;确定评论中的正面或负面情绪&…...

指针(3)

C语言昂&#xff0c;指针昂&#xff0c;最喜欢的一集&#xff0c;小时候学这一课我直接取地址了。上一篇博客给大家讲解了不同类型的指针变量的大小&#xff0c;今天来给大家讲解一下根据其所产生的一些性质。&#xff08;往期回顾&#xff1a;指针&#xff08;2&#xff09;-C…...

外汇天眼:我碰到外汇投资骗局了吗?学会这5招,轻松识别外汇诈骗黑平台!

近年来外汇市场因为交易量大、流动性大、不容易被控盘、品种简单、风险相对低等特色&#xff0c;因此吸引不少投资人青睐&#xff0c;成为全球金融市场的热门选择。 然而&#xff0c;市面上充斥许多诈骗集团设立的黑平台&#xff0c;也打着投资外汇的名义行骗&#xff0c;不免会…...

一文解析子网掩码和默认网关,成为网络设置达人

随着互联网的普及&#xff0c;越来越多的人开始接触并使用电脑和网络。然而&#xff0c;对于很多初学者来说&#xff0c;网络设置中的子网掩码和默认网关是两个相对陌生的概念。今天&#xff0c;我们就来深入解析这两个概念&#xff0c;让你轻松掌握网络设置技巧&#xff01; …...

二分查找法详解(6种变形)

前言 在之前的博客中&#xff0c;我给大家介绍了最基础的二分查找法&#xff08;没学的话点我点我&#xff01;&#xff09; 今天我将带大家学习二分法的六种变形如何使用&#xff0c;小伙伴们&#xff0c;快来开始今天的学习吧&#xff01; 文章目录 1&#xff0c;查找第一个…...

uniapp uview 页面多个select组件回显处理,默认选中

<view class"add-item column space-around" click"selectClick(1)"><text class"w-s-color-3 f-28">商品分类</text><view class"w-100 space-between"><!-- 第一个参数为你的单选数组&#xff0c;第二个…...

linux中playbook的控制语句

本章主要介绍 playbook中的控制语句。 使用 when 判断语句 block-rescue判断 循环语句 一个play中可以包含多个task&#xff0c;如果不想所有的task全部执行&#xff0c;可以设置只有满足某个 条件才执行这个task&#xff0c;不满足条件则不执行此task。本章主要讲解when 和 …...

MongoDB介绍

一、MongoDB介绍 1.1 mongoDB介绍 MongoDB 是由C语言编写的&#xff0c;是一个基于分布式文件存储的开源数据库系统。 在高负载的情况下&#xff0c;添加更多的节点&#xff0c;可以保证服务器性能。 MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB …...

再看参数校验

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 写一个接口&#xff0c…...

计算机存储术语: 扇区,磁盘块,页

扇区(sector) 硬盘的读写以扇区为基本单位。磁盘上的每个磁道被等分为若干个弧段&#xff0c;这些弧段称之为扇区。硬盘的物理读写以扇区为基本单位。通常情况下每个扇区的大小是 512 字节。linux 下可以使用 fdisk -l 了解扇区大小&#xff1a; $ sudo /sbin/fdisk -l Disk …...

解决IDEA编译/启动报错:Abnormal build process termination

报错信息 报错信息如下&#xff1a; Abnormal build process termination: "D:\Software\Java\jdk\bin\java" -Xmx3048m -Djava.awt.headlesstrue -Djava.endorsed.dirs\"\" -Djdt.compiler.useSingleThreadtrue -Dpreload.project.path………………很纳…...

Jetpack DataStore

文章目录 Jetpack DataStore概述DataStore 对比 SP添加依赖库Preferences DataStore路径创建 Preferences DataStore获取数据保存数据修改数据删除数据清除全部数据 Proto DataStore配置AndroidStudio安装插件配置proto文件创建序列化器 创建 Proto DataStore获取数据保存数据修…...

在Portainer创建Nginx容器并部署Web静态站点实现公网访问

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…...

泛微e-cology XmlRpcServlet文件读取漏洞复现

漏洞介绍 泛微新一代移动办公平台e-cology不仅组织提供了一体化的协同工作平台,将组织事务逐渐实现全程电子化,改变传统纸质文件、实体签章的方式。泛微OA E-Cology 平台XmRpcServlet接口处存在任意文件读取漏洞&#xff0c;攻击者可通过该漏洞读取系统重要文件 (如数据库配置…...

当下流行的直播技术demo演示

nginx-http-flv-module&#xff08;更新不是很频繁&#xff09; SRS: https://ossrs.net/lts/zh-cn/&#xff08;独立官网&#xff0c;目前最新稳定版version5&#xff09; 基于SRS搭建直播demo演示&#xff1a; 一、搭建流媒体服务器 参见官网&#xff1a;https://ossrs.ne…...

Zabbix自动发现并注册已安装agent的主机

先在被监控主机上安装好zabbix-agent 然后登录zabbix网页 点击发现动作后会出现第三步 然后编辑操作&#xff0c;发现后加入到主机组群 然后编辑发现规则 然后就可以在主机列表中看到被发现的主机。...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...