【C语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化
引言
动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。
✨ 猪巴戒:个人主页✨
所属专栏:《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)
申请内存块,
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;
}
动态内存空间位置
动态内存函数是在堆区开辟内存空间的。我们一般的局部变量、形式参数都是存放在栈区。
内存泄漏问题
什么是内存泄漏?
我们创建的局部变量,数据会在函数结束时释放。
动态内存空间存放临时使用的数据,这些数据不必等到函数结束时释放,而是需要时随时开辟,不需要时,随时释放。动态开辟的内存使用完是要进行释放的,如果不对内存进行释放,那么开辟的动态内存就会被之前的数据占据,这部分的内存就无法使用,相当于丢失了内存。因此我们把这类问题叫做内存泄漏。
下面的代码如果吧进行释放,那么就会一直占据内存空间。
通过释放动态内存,内存可以被重新调用
最后我们加了一步,p = NULL,p的内存空间已经被释放了还给操作系统了,但是p还是原来的地址,我们通过p就会使用到一个已经释放的内存,这就会导致野指针问题。为了避免这种情况,我们将p的地址去掉就可以了。
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;
}
calloc
void* calloc (size_t num, size_t size)
num
开辟空间元素的个数
size
空间中每个元素的大小。
- 函数的功能为num个大小为size 的元素开辟一块空间,并且把空间的每个字节初始化为0
- calloc和malloc的区别在于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;
}
realloc
void* realloc (void* ptr, size_t size)
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;
}
realloc如何开辟动态内存空间
realloc在调整内存空间的是存在两种情况。
第一种情况,原空间之后有足够大的空间。当要进行调整的内存空间后面有多余的40个字节空间,那么就可以直接开辟向后面开辟40个字节的空间,然后放回起始位置的地址,这里指的是0的地址。
第二种情况,原空间之后没有足够大的空间。要调整的内存空间后面不足以存放40个字节的空间,那么就要重新找到一个新的地址(可以存放80个字节)开辟,开辟成功后返回起始位置的地址。
注意:如果需要开辟的空间过大,是会开辟失败的,开辟失败,realloc返回空指针,所以要检查空指针。
这里把realloc开辟的空间换成8000,来实现第二种情况。
动态版本的通讯录
动态:
要实现通讯录容量的动态化,要实现两个功能
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是既开辟空间,并进行初始化。malloc和realloc地区别就在于是否对开辟的空间初始化。
开辟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;
}
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语言】动态内存管理基础知识——动态通讯录,如何实现通讯录容量的动态化
引言 动态内存管理的函数有:malloc,calloc,ralloc,free,本文讲解动态内存函数和使用,如何进行动态内存管理,实现通讯录联系人容量的动态化,对常见动态内存错误进行总结。 ✨ 猪巴戒:个人主页✨ 所属专栏:《C语言进阶》…...
Centos9(Stream)配置Let‘s Encrypt (免费https证书)
1. 安装snap,用来安装certbot: 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)
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…...
嵌入式科普(5)ARM GNU Toolchain相关概念和逻辑
一、目的/概述 二、资料来源 三、逻辑和包含关系 四、Arm GNU Toolchain最常用的命令 嵌入式科普(5)ARM GNU Toolchain相关概念和逻辑 一、目的/概述 对比高集成度的IDE(MDK、IAR等),Linux开发需要自己写Makefile等多种脚本。eclipse、Visual Studio等需要了解预处…...
Elasticsearch:什么是文本分类?
文本分类定义 - text classification 文本分类是一种机器学习,它将文本文档或句子分类为预定义的类或类别。 它分析文本的内容和含义,然后使用文本标签为其分配最合适的标签。 文本分类的实际应用包括情绪分析(确定评论中的正面或负面情绪&…...
指针(3)
C语言昂,指针昂,最喜欢的一集,小时候学这一课我直接取地址了。上一篇博客给大家讲解了不同类型的指针变量的大小,今天来给大家讲解一下根据其所产生的一些性质。(往期回顾:指针(2)-C…...
外汇天眼:我碰到外汇投资骗局了吗?学会这5招,轻松识别外汇诈骗黑平台!
近年来外汇市场因为交易量大、流动性大、不容易被控盘、品种简单、风险相对低等特色,因此吸引不少投资人青睐,成为全球金融市场的热门选择。 然而,市面上充斥许多诈骗集团设立的黑平台,也打着投资外汇的名义行骗,不免会…...
一文解析子网掩码和默认网关,成为网络设置达人
随着互联网的普及,越来越多的人开始接触并使用电脑和网络。然而,对于很多初学者来说,网络设置中的子网掩码和默认网关是两个相对陌生的概念。今天,我们就来深入解析这两个概念,让你轻松掌握网络设置技巧! …...
二分查找法详解(6种变形)
前言 在之前的博客中,我给大家介绍了最基础的二分查找法(没学的话点我点我!) 今天我将带大家学习二分法的六种变形如何使用,小伙伴们,快来开始今天的学习吧! 文章目录 1,查找第一个…...
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"><!-- 第一个参数为你的单选数组,第二个…...
linux中playbook的控制语句
本章主要介绍 playbook中的控制语句。 使用 when 判断语句 block-rescue判断 循环语句 一个play中可以包含多个task,如果不想所有的task全部执行,可以设置只有满足某个 条件才执行这个task,不满足条件则不执行此task。本章主要讲解when 和 …...
MongoDB介绍
一、MongoDB介绍 1.1 mongoDB介绍 MongoDB 是由C语言编写的,是一个基于分布式文件存储的开源数据库系统。 在高负载的情况下,添加更多的节点,可以保证服务器性能。 MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB …...
再看参数校验
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 写一个接口,…...
计算机存储术语: 扇区,磁盘块,页
扇区(sector) 硬盘的读写以扇区为基本单位。磁盘上的每个磁道被等分为若干个弧段,这些弧段称之为扇区。硬盘的物理读写以扇区为基本单位。通常情况下每个扇区的大小是 512 字节。linux 下可以使用 fdisk -l 了解扇区大小: $ sudo /sbin/fdisk -l Disk …...
解决IDEA编译/启动报错:Abnormal build process termination
报错信息 报错信息如下: 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静态站点实现公网访问
🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,…...
泛微e-cology XmlRpcServlet文件读取漏洞复现
漏洞介绍 泛微新一代移动办公平台e-cology不仅组织提供了一体化的协同工作平台,将组织事务逐渐实现全程电子化,改变传统纸质文件、实体签章的方式。泛微OA E-Cology 平台XmRpcServlet接口处存在任意文件读取漏洞,攻击者可通过该漏洞读取系统重要文件 (如数据库配置…...
当下流行的直播技术demo演示
nginx-http-flv-module(更新不是很频繁) SRS: https://ossrs.net/lts/zh-cn/(独立官网,目前最新稳定版version5) 基于SRS搭建直播demo演示: 一、搭建流媒体服务器 参见官网:https://ossrs.ne…...
Zabbix自动发现并注册已安装agent的主机
先在被监控主机上安装好zabbix-agent 然后登录zabbix网页 点击发现动作后会出现第三步 然后编辑操作,发现后加入到主机组群 然后编辑发现规则 然后就可以在主机列表中看到被发现的主机。...
Jtti:linux搭建开源ldap服务器的方法
搭建开源LDAP服务器是一种用于集中管理用户身份认证和授权信息的方法。在Linux系统上,OpenLDAP是一个流行的开源LDAP实现,可以用于搭建LDAP服务器。以下是搭建OpenLDAP服务器的基本步骤: 步骤一:安装OpenLDAP 安装OpenLDAP软件包&…...
Gazebo GUI模型编辑器
模型编辑器 现在我们将构建我们的简单机器人。我们将制作一个轮式车辆,并添加一个传感器,使我们能够让机器人跟随一个斑点(人)。 模型编辑器允许我们直接在图形用户界面 (GUI) 中构建简单的模型。对于更复…...
pycharm运行正常,但命令行执行提示module不存在的多种解决方式
问题描述 在执行某个测试模块时出现提示,显示自定义模块data不存在,但是在PyCharm下运行正常。错误信息如下: Traceback (most recent call last):File "/run/channelnterface-autocase/testcases/test_chanel_detail.py", line 2…...
GBASE南大通用GBase 8a ODBC的安装文件
GBASE南大通用GBase 8a ODBC 体系结构是基于五个组件,在下图中所示: GBase 8a ODBC 体系结构图 应用 应用是通过调用 ODBC API 实现对 GBase 数据访问的程序。应用使用标准的 ODBC 调用与驱动程序管理器通信。应用并不关心数据存储在哪里ÿ…...
重新配置torch1.8 cuda11.1 torchtext0.9.0虚拟Pytorch开发环境
这里写目录标题 起因发现选择安装cuda 11.1核对下自己的显卡是否支持下载该版本的CUDACUDA下载地址CUDA安装过程 在anaconda中创建一个虚拟环境1.以下是环境的配置过程2.查看虚拟环境列表3.激活虚拟环境 安装torch和torchtext包的过程1.输入下面这句代码,就可以直接…...
【动画图解】一次理清九大排序算法!面试官问到再也不慌!
排序算法 交换排序 冒泡排序快速排序 插入排序 直接插入排序希尔排序 选择排序 简单选择排序堆排序 归并排序基数排序桶排序 一、冒泡排序 冒泡排序是一种简单的交换排序算法,以升序排序为例,其核心思想是: 从第一个元素开始,…...
组播地址段及其作用
作用 组播(Multicast)传输:在发送者和每一接收者之间实现点对多点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也只需复制一份的相同数据包。它提高了数据传送效率。减少了骨干网络出现拥塞的可能性。 地址段 组播协议的地址在 IP 协议中属于 D 类…...
Vue+ElementUI前端添加展开收起搜索框按钮
1、搜索框添加判断 v-if"advanced" <el-form-item label"创建日期" v-if"advanced"><el-date-pickerv-model"daterangeLedat"size"small"style"width: 240px"value-format"yyyy-MM-dd"type&q…...
速盾网络:sdk游戏盾有什么作用?
速盾cdn是一款非常优秀的CDN加速服务,它能够帮助游戏开发者们提升游戏的性能和稳定性。其中,速盾cdn的sdk游戏盾是其一项非常实用的功能,它能够为游戏提供更加稳定和快速的网络连接。 首先,让我们来了解一下什么是sdk游戏…...
理解BeEF的架构
BeEF的组件和工作原理BeEF(The Browser Exploitation Framework)是一款用于浏览器渗透测试和漏洞利用的强大工具。它由多个组件组成,这些组件协同工作以实现对受害者浏览器的控制和攻击。本文将深入探讨BeEF的各个组件和其工作原理࿰…...
wordpress站内301/qq刷赞网站推广
这一周我们小组进行了JSON转换和数据库动态转换以及用户界面的编写等工作,我负责编写用户查看发布信息界面和申请发布界面。遇到的问题有element ui的card组件分页和表单数据与页面数据一起传到后端。 发布信息以el-card的形式展现在页面上,结合el-r…...
点击最高的模板网站/搜索引擎优化主要包括
先说下我这个有啥好处 主要是能够自定义设置裁剪图片的比例.比如 :今天产品 给你说裁剪 成16:9 的图片. 你做好了 OK 明天 产品又和你说 裁剪成10:7 的图片 ,你是不是要吐血, 我这里只要设置一行代码就可以改变裁剪的比例.OK 还是老规矩 先上效果图 看看啥样子好了 不扯其他的了…...
汽车之家网站是谁做的/公司员工培训内容有哪些
1、遇到不想回答的问题,直视对方的眼睛,微笑、沉默。- 2、走路抬头挺胸,心情不好时,不想跟人招呼,点头微笑,径直走过。- 3、请记得,好朋友的定义是:你混的好,他打心眼里为…...
公司自己的网站叫什么/网站搭建需要什么技术
本文就来看看在Spring Boot中MyBatis要如何使用。 工程创建 首先创建一个基本的Spring Boot工程,添加Web依赖,MyBatis依赖以及MySQL驱动依赖,如下: 最简单的SpringBoot整合MyBatis教程 创建成功后,添加Druid依赖&#…...
阿里云心选建站/seo技术教程博客
01 chromium较全的开关选项说明 https://peter.sh/experiments/chromium-command-line-switches/ 很多命令行开关项在这个文件中 src\chrome\common\chrome_switches.cc02 如何取消一些不感兴趣的工程 https://blog.csdn.net/dopi/article/details/27662959...
做美国市场哪个网站好/百度影音在线电影
【问题】 在进入docker镜像后,我们一般缺少vim,需要安装,但是在更新的过程中往往会卡在working 的进度为0上。 【解决方法】 mv /etc/apt/sources.list /etc/apt/sources.list.bak echo "deb http://mirrors.163.com/debian/ jessie …...