Cyuyan中的动态内存管理!!(对后面学习数据结构至关重要)

news2024/10/6 8:15:06

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、为什么要进行动态内存分配
  • 二、实现动态内存分配的三种库函数
    • (一)、malloc函数
    • (二)、calloc函数
    • (三)、realloc函数
    • (四)、free函数
  • 三、常见的动态内存的错误
  • 四、有关动态内存分配的经典题型分析
  • 五、柔性数组的介绍(前面没有见到过)
    • (一)、柔性数组的概念
    • (二)、柔性数组的特点
    • (三)、柔性数组的使用与优势
  • 六、C语言中内存区域的划分
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:


提示:以下是本篇文章正文内容,下面案例可供参考
本文主要介绍C语言中的动态内存管理相关内容,它是后面学习数据结构的基础,至关重要。在本文中首先会介绍为什么要使用动态内存,它有什么好处,然后介绍三种实现动态内存分配的库函数(malloc、calloc、realloc);而后介绍在动态内存分配中常常出现的几种问题;紧接着分析几道有关动态内存分配的习题,再者介绍一下柔性数组(属于新概念型),最后讲一下C语言中程序内存区域的划分

一、为什么要进行动态内存分配

  • 我们在学习动态内存分配之前,我们之前用的内存开辟方式都是非动态内存开辟,它们都是在栈区开辟的内存空间
int val=20;//在栈空间上开辟四个字节的空间
char arr[10]={0};///在栈空间上开辟10个字节的连续空间

上述在栈区开辟的空间有两个特点:
一是空间开辟大小是固定的;
二是数组在声明的时候,必须指定数组的长度,数组空间一旦确定了,大小就不能调整了。

  • 但是我们对于空间的需求,不仅仅是上述的情况,有时候我们需要的空间大小要根据程序允许需要才知道使用多大的空间,那么数组的编译时开辟空间的方式就不能满足了,故而我们引入了动态开辟,让我们自己可以申请和释放空间,形式就比较灵活了。

二、实现动态内存分配的三种库函数

(一)、malloc函数

头文件是<stdlib.h>

  • 函数原型:
void* malloc (size_t size);
  • 关于此函数注意以下几点:
    这个函数是向内存申请一块连续可用的空间,并返回这块空间的指针;
    size的单位是字节;
    如果开辟成功,则返回一个开辟好的空间的指针;
    如果开辟失败,则返回一个NULL指针,因此我们在使用malloc函数后一定要进行检查是否开辟成功;
    该函数的返回值类型是void*,所以我们用malloc函数后要根据需要进行强制类型转换成我们所需要的类型;
    如果size为0,malloc行为是未定义的,取决于编译器。
#include<stdlib.h>
#include<string.h>
void test()
{
int*p=(int*)malloc(20);//动态申请空间
//判断是否申请成功
if(p==NULL)
{
perror("malloc");//告诉大家是malloc那一步出的问题
return ;

(二)、calloc函数

头文件是<stdlib.h>

  • 函数原型:
void* calloc (size_t num, size_t size);
  • 关于此函数注意以下几点:
    函数的功能是为num个字节为size的元素开辟一块空间,并把空间的每个字节初始化为0;
    与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化全为0
#include <stdio.h>
 #include <stdlib.h>
 int main()
 {
 int *p = (int*)calloc(10, sizeof(int));
 if(NULL != p)
 {
 int i = 0;
 for(i=0; i<10; i++)
 {
 printf("%d ", *(p+i));
 }
 }
 free(p);
 p = NULL;
 return 0;
 }

运行结果如下:
在这里插入图片描述
故而我们如果要对申请的内存空间要求初始化的时候,我们可以调用calloc函数来实现。

(三)、realloc函数

头文件是<stdlib.h>

  • 函数原型:
void* realloc (void* ptr, size_t size);
  • 关于此函数注意以下几点:
    其一:realloc函数的出现让动态内存管理更加灵活啦,有时候我们会发现申请的空间太小了,或者申请的空间太大了,那么未来合理的使用内存,我们一定会对内存的大小进行一定的调整,realloc函数可以实现对动态开辟的内存大小进行调整;
    其二:上述函数原型中,ptr是要调整内存的起始地址,size是调整之后的大小,返回值是调整之后内存的起始位置;
    其三:这个函数在调整原来内存空间大小的基础上,还有将原来内存中的数据移动到新的空间里去;
  • realloc在调整内存空间的时候存在以下两种调整情况
    其一:原有空间之后足够大;
#include<stdlib.h>
int main()
{
int*ptr=(int*)malloc(20);
if(ptr==NULL)
{
return 1;
}
int*tmp=(int*)realloc(ptr,40);
return 0;
}

我们对ptr指针进行扩容处理
在这里插入图片描述
ptr后面的空间足够大,我们可以直接进行扩容,这样原来空间的数据不发生变化。
其二:原有空间之后没有足够大的空间
在这里插入图片描述
遇到这种情况我们就要注意了!!原有空间之后没有足够大的空间,它的扩容方法为:在堆空间上另外找一个合适大小的空间来使用,这样函数返回的是一个新的内存地址,这还涉及到将原有的数据粘贴到新空间的问题
我们在写代码的时候最好重新定义一个指针将扩容后的结果放到新的指针里,这样是为了避免扩容失败后原来的数据丢失

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int*ptr=(int*)malloc(20);
if(ptr==NULL)
{
return 1;
}
int*p=NULL;//重新定义一个变量
p=(int*)realloc(ptr,1000);
if(p==NULL)
{
malloc("realloc");//显示错误原因
return 1;
}
ptr=p;
free(ptr);
ptr=NULL;
}

(四)、free函数

头文件是<stdlib.h>
只要涉及使用到以上三中动态内存分配函数,当我们不用到我们申请的内存空间后,一定要通过free函数释放掉, 并将指针置空(避免出现野指针问题)(养成好习惯)

  • 函数原型:
void free(void*ptr);
  • 关于free函数注意以下几点:
    如果参数ptr指向的空间不是动态开辟的,那么free函数的行为是未定义的;
    如果参数ptr是NULL指针,那么函数相当于什么也没做。
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
 int main()
{
 int num = 0;
 scanf("%d", &num);
 int arr[num] = {0};
 int* ptr = NULL;
 ptr = (int*)malloc(num*sizeof(int));
 if(ptr==NULL)
 {
 perror("malloc");
 return 0;
 }
 int i = 0;
 for(i=0; i<num; i++)
 {
 *(ptr+i) = 0}
 free(ptr);//释放ptr所指向的动态内存
 ptr=NULL;//避免野指针问题。
 return 0;
}

三、常见的动态内存的错误

  • 对NULL指针的解引用操作
void test()
{
int*p=(int*)malloc(20);
*p=20;//如果p动态内存没有开辟成功就会出现对空指针的解引用问题
free(p);
p=NULL;
}

在这里插入图片描述
所以我们在对p进行解引用前,一定要判断一下是否内存开辟成功了,如果开辟成功的话,才能进行接下来的一系列操作,如果没有成功,应该立即结束程序
修改如下:

void test()
{
int*p=(int*)malloc(20);
if(p==NULL)
{
perror("malloc");//告诉大家是malloc那一步出的问题
return ;
}
*p=20;//如果p动态内存没有开辟成功就会出现对空指针的解引用问题
free(p);
p=NULL;
return ;
}

这样就完美啦,不会出现对空指针进行解引用的问题。

  • 对动态开辟空间的越界访问
 void test()
 {
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 perror("malloc");
 return;
 }
 for(i=0; i<=10; i++)
 {
  *(p+i) = i;//当i等于10的时候很显然出现了越界访问。
 }
 free(p);
 p=NULL;//避免出现野指针问题
 }

对于以上代码,我们将p指针所指向的空间开辟成40个字节大小,但是我们下面用for进行遍历赋值的时候,当i等于10的时候,我们越界访问了未开辟的空间,这就出现了问题。
我们只能访问到i=9。

  • 对非动态开辟内存使用free释放,程序会出现卡死的现象,是比较严重的问题,一定要避免
#include<stdio.h>
#include<stdlib.h>
void test()
{
int a=10;
int*p=&a;
free(p);
}
int main()
{
test();
}

在这里插入图片描述
很显然局部变量a是存在于栈区的,我们free只能释放堆区(动态开辟的内存),不能释放栈区(非动态开辟的内存)的内存,否则会出现程序卡死的现象

  • 使用free释放一块动态开辟内存的一部分
#include<stdio.h>
#include<stdlib.h>
void test()
 {
 int *p = (int *)malloc(100);
 if(p==NULL)
 {
 perror("malloc");
 return ;
 }
 p++;
 free(p);//p不再指向内存的起始位置
 }
 int main()
 {
 test();
 }

在这里插入图片描述

这里我们对p指针进行了自加加运算后,p就不再指向动态内存的起始位置,这样我们释放的时候就指释放了动态开辟内存的一部分,这一定会出现程序卡死的。所以我们再进行指针操作的时候一定要保存动态内存的起始位置。为了方便我们后续的内存释放,修改代码如下:

#include<stdio.h>
#include<stdlib.h>
void test()
 {
 int *p = (int *)malloc(100);
 if(p==NULL)
 {
 perror("malloc");
 return ;
 }
 int*j=p;//记录动态内存开辟的起始位置。
 p++;
 free(j);//p不再指向内存的起始位置
 }
 int main()
 {
 test();
 }
  • 对同一块动态内存多次释放
#include<stdio.h>
#include<stdlib.h>
 void test()
 {
 int *p = (int *)malloc(100);
 free(p);
 free(p);
 }
 int main()
 {
 test();
 }

在这里插入图片描述
我们对动态开辟的p指针进行两次内存释放,这会出现程序卡死的现象,如果我们在第一次释放后,将p置为空指针,那么再释放多少次都不会报错

#include<stdio.h>
#include<stdlib.h>
 void test()
 {
 int *p = (int *)malloc(100);
 free(p);
 p=NULL;
 free(p);
 }
 int main()
 {
 test();
 }

在这里插入图片描述

但是这种操作没有什么必要,我们只需记住,不能对同一块内存空间进行多次释放即可

  • 动态开辟内存忘记释放(造成内存泄漏)
#include<stdio.h>
#include<stdlib.h>
void test()
 {
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
 }
 int main()
 {
 test();
 }

在这个程序中,我们调用完test函数后,在test函数里面动态开辟的内存空间,没有得到释放,这样就会造成内存泄漏的问题,切记:动态内存开辟的空间一定要释放,并且正确释放,修改如下:

#include<stdio.h>
#include<stdlib.h>
void test()
 {
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
 free(p);
 p=NULL;//注意释放完要置空,避免出现野指针的问题
 }
 int main()
 {
 test();
 }

四、有关动态内存分配的经典题型分析

  • 练习1:分析以下代码有什么问题
#include<stdio.h>
#include<stdlib.h>
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");//这里存在对NULL指针进行解引用,程序会崩溃,而且还存在内存泄露
	printf(str);
}
int main()
{
	test();
	return 0;
}

这个代码存在大问题,运行的时候出现了崩溃的现象,主要存在两处内存错误,第一个是对空指针进行了解引用操作,第二个是没有释放动态开辟的内存空间,最主要的原因是我们在调用GetMemory函数的时候,没有传指针的地址,这样是无法给str进行内存分配的,修改方法有两种
法1:使用二级指针方法:

#include<stdio.h>
#include<stdlib.h>
void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}
void test(void)
{
	char* str = NULL;
	GetMemory(&str);//传入的是指针str的地址
	strcpy(str, "hello world");
	printf(str);
	free(str);//防止内存泄漏
	str = NULL;//避免野指针问题
}
int main()
{
	test();
	return 0;
}

运行结果如下:
在这里插入图片描述
在调用GetMemory函数的时候我们传的是str指针的地址,在接收的时候我们用的是二级指针这样就可以有效的对str进行内存分配,使用完毕后,我们free掉str,并把str进行置空操作,这样有效的避免了内存泄漏和野指针问题。
法2:使用return返回的方法:

#include<stdio.h>
#include<stdlib.h>
char* GetMemory()
{
	char*p = (char*)malloc(100);

	return p;
}
void test(void)
{
	char* str = NULL;
	str=GetMemory();
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}
int main()
{
	test();
	return 0;
}

运行结果如下:
在这里插入图片描述

在这里我们重新定义了GetMemory函数的返回类型,改为char*类型,这样我就可以用str来接收,间接性的对str进行了动态内存分配,最后将str给free掉,并将str置空,避免了内存泄漏与野指针问题。

  • 练习2:分析以下代码有什么问题:
#include<stdio.h>
#include<stdlib.h>
char* GetMemory(void)
{
	char p[] = "hello world";//在栈区,这块空间只在函数运行时候才分配
	return p;
}
void test()
{
	char* str = NULL;
	str = GetMemory();//野指针问题,虽然放着数组首元素地址,但是不能使用这块空间
	printf(str);

}
int main()
{
	test();
	return 0;
}

运行结果如下:
在这里插入图片描述
这段代码咱们预期结构是hello world,但是却打印出来乱码,这里存在着一个大问题——野指针问题。我们调用GetMemory函数的时候,给数组p分配的空间是栈区的空间,它的特点是出了GetMemory函数后,这块内存会自动地返还给操作系统,我们返回地只是这块空间地起始地址,暂存放在str指针中而我们接下里要对str进行访问(越界访问),这就是野指针问题(指针指向了一块不属于自己地空间),所以打印出来地东西是未知的,随机的,因为我们访问的不是我们能掌握的空间。
故而,返回栈空间地址的问题,一定会引起野指针的问题,但返回变量的值确实可以的(因为这个值会放到寄存器中临时存储,不会销毁)

#include<stdio.h>
int test()
{
int a=10;
return a;
}
int main()
{
int n=test();
printf("%d\n",n);
return 0;

}

在这里插入图片描述
在这里插入图片描述
在这里我们调用完test函数后,a是个局部变量,它地内存空间会自动地返回操作系统,但是它地值会被放在寄存器中存储起来,这时候,我们就可以将寄存器地值赋给n变量。

而我们返回a地址就会出问题;

#include<stdio.h>
int* test()
{
int a=10;
return &a;
}
int main()
{
int* n=test();
printf("%d\n",*n);
return 0;

}

在这里插入图片描述
这里虽然也是打印出10来,但是这是巧合,
如果 我们在打印之前再打印一个hehe,这样地话就会显现出问题来

#include<stdio.h>
int* test()
{
int a=10;
return &a;
}
int main()
{
int* n=test();
printf("hehe\n");
printf("%d\n",*n);
return 0;
}

在这里插入图片描述
这时候值就变成了5,因为变量a在出test函数后它所指向地空间就被释放掉了,n指针只保存了起始地址,但是它指向地空间是未知地,所以进行解引用后打印出来地东西也是未知的(典型地野指针问题)!!

  • 练习3:分析以下代码有什么问题:
#include<stdio.h>
#include<stdlib.h>
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);

}
void test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}
int main()
{
	test();
	return 0;
}

在这里插入图片描述
这段代码分析起来与练习1类似。都是运用了二级指针来对str进行动态内存分配,但是此代码最大的问题就是忘记释放掉开辟出来地动态内存空间,存在内存泄漏地问题。修改如下:

#include<stdio.h>
#include<stdlib.h>
void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);

}
void test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
	free(str);//避免内存泄漏
	str=NULL;//避免野指针问题
}
int main()
{
	test();
	return 0;
}
  • 练习4:分析以下代码有什么问题:
#include<stdio.h>
#include<stdlib.h>
void test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);//野指针问题,free完一定要置为空指针,避免野指针问题,防止出现野指针问题
	if (str != NULL)
	{
		strcpy(str, "world");//形成非法访问,野指针问题
		printf(str);
	}

}
int main()
{
	test();
	return 0;
}

这段代码存在两个主要问题:
其一:造成野指针问题,进行了非法访问,我们将str指针释放掉后,没有将str指针置空,造成了对访问不属于自己空间地行为(野指针问题)。
其二:没有对malloc开辟出来的而空间进行判断是否为空:
在这里插入图片描述
修改后的代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void test(void)
{
	char* str = (char*)malloc(100);
	if(str==NULL)
	{
	perror("malloc");
	return ;
	}
	strcpy(str, "hello");
	free(str);//野指针问题,free完一定要置为空指针,避免野指针问题,防止出现野指针问题
	str=NULL;
	if (str != NULL)
	{
		strcpy(str, "world");//形成非法访问,野指针问题
		printf(str);
	}

}
int main()
{
	test();
	return 0;
} 

五、柔性数组的介绍(前面没有见到过)

(一)、柔性数组的概念

  • 在C99中,结构体的最后一个成员允许是未知大小的数组,这就叫做”柔性数组“成员
  • 它具体有两种定义形式:
    其一:
struct s1
{
	int n;
	char c;
	double d;
	int arr[];//在结构体中,最后一个成员,未知大小,称为柔性数组

};

其二:

struct s2
{
	int n;
	char c;
	double d;
	int arr[0];//这里也是未知大小的数组!
};

(二)、柔性数组的特点

  • 结构中的柔性数组成员前面必须至少有一个其他成员;
  • sizeof返回的这种结构大小不包括柔性数组的内存
#include<stdio.h>
typedef struct st_type
{
int i;
int a[0];//柔性数组
}type_a;
int main()
{
printf("%zd\n",sizeof(type_a));
}

运行结果如下:
在这里插入图片描述
很显然一个int类型就占据4个字节,在利用sizeof计算结构体内存大小的时候,没把柔性数组所占的内存大小算进去。

  • 包含柔性数组的成员的结构用malloc()函数进行内存的动态分配,并且分配的内存大小应该大于结构的大小,以适应柔性数组的预期大小!

(三)、柔性数组的使用与优势

  • 柔性数组的使用代码如下:
    代码1:
#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
{
int i;
int a[0];//柔性数组
}type_a; 
int main()
 {
int i = 0;
type_a* p=(type_a*)malloc(sizeof(type_a)+100*sizeof(int));//分配内存空间的时候,额外分配内存空间,以适应柔性数组的预期大小。

p->i = 100;
//对柔性数组进行赋值使用
for(i=0; i<100; i++)
{
p->a[i] = i;
}
free(p);
p=NULL;
return 0;
}

当然我们也可以利用指针型成员来代替柔性数组,达到同样的效果,不过要进行两次动态内存分配,释放的时候也应该两次释放。代码如下:
代码2:

#include <stdio.h>
#include <stdlib.h>
typedef struct st_type
 {
 int i;
 int *p_a;//指针型成员
 }type_a;
int main()
 {
 type_a *p = (type_a *)malloc(sizeof(type_a));//直接给结构体开辟空间,没有额外开辟空间。
 //对结构体成员进行操作:
 p->i = 100;
 p->p_a = (int *)malloc(p->i*sizeof(int));//为指针成员进行空间的开辟,类同于对柔性数组进行内存空间的开辟
for(i=0; i<100; i++)
 {
 p->p_a[i] = i;
 }
 for(i=0; i<100; i++)
 {
 p->p_a[i] = i;
 }
 //对开辟的空间进行内存释放,注意释放顺序由里向外释放,否则会造成指针地址的丢失
 free(p->p_a);
 p->p_a=NULL;//避免野指针问题
 free(p);
 p=NULL;//避免野指针问题
 return 0;
 }
  • 两段代码可以达到同样的效果,但是我们会优先选择代码1,即利用柔性数组来进行操作,这就涉及到了使用柔性数组的优势,它有两个好处:
    其一:方便内存的释放。如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。⽤⼾调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。
    其二:有利于访问速度。连续的内存有益于提高访问速度,也有益于减少内存碎片。

六、C语言中内存区域的划分

在这里插入图片描述
由上面图可以知道,在C/C++程序中我们计算机中的内存空间被分成了六个区域,分别为内核空间、栈区、内存映射段、堆区、数据段(静态区)、代码段。我们结合上面代码分别介绍这六个区域分别存储什么内容。

  • 内核空间:这块内存空间,是留给操作系统的,用户所写的代码不能在这段空间里面读写于存储。
  • 栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内存容量有限。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。例如上面代码中的
    在这里插入图片描述

所定义的局部变量都存放在栈区中。

  • 内存映射段:主要涉及到文件映射、动态库、匿名映射相关内容的存储,这块后面写操作系统类文章时再详细书写,在这做一下了解。

  • 堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配⽅式类似于链表。换言之我们本文所讲的动态内存申请都是从堆区里面申请的,它常用malloc、calloc、realloc函数进行申请,使用完后,我们要用free函数手动释放,以免造成内存泄漏问题。如果没有释放,那么再程序最终结束的时候会被OS回收。
    上述代码中的用malloc、calloc、realloc,申请的在堆区中存放的
    在这里插入图片描述
    存在于堆区。

  • 数据段(静态区):(static)存放全局变量、静态数据。程序结束后由系统释放,上述代码中的
    在这里插入图片描述在这里插入图片描述
    全局变量与用static修饰的变量存放于数据段中。

  • 代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。
    在这里插入图片描述
    代码总的常量字符串,或者进入计算器中转换成的二进制代码都存放于代码段中。

总结

本文主要介绍C语言中的动态内存管理相关内容。在本文中首先会介绍为什么要使用动态内存,它有什么好处,然后介绍三种实现动态内存分配的库函数(malloc、calloc、realloc);而后介绍在动态内存分配中常常出现的几种问题;紧接着分析几道有关动态内存分配的习题,再者介绍一下柔性数组(属于新概念型),最后讲一下C语言中程序内存区域的划分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1900342.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt5.9.9 关于界面拖动导致QModbusRTU(QModbusTCP没有测试过)离线的问题

问题锁定 参考网友的思路&#xff1a; Qt5.9 Modbus request timeout 0x5异常解决 网友认为是Qt的bug&#xff0c; 我也认同&#xff1b;网友认为可以更新模块&#xff0c; 我也认同&#xff0c; 我也编译了Qt5.15.0的code并成功安装到Qt5.9.9中进行使用&#xff0c;界面拖…

可验证算法在招投标领域的专家“盲抽”中的标段识别码加密应用研究

摘要 在招投标过程中&#xff0c;标段&#xff08;包&#xff09;识别码的安全性至关重要。本文提出了一种基于可验证算法的标段识别码加密方法&#xff0c;以确保其在专家“盲抽”过程中的保密性和可信性。通过对不同表的标段识别码进行全量加密&#xff0c;并通过匹配验证其…

绿色金融相关数据合集(2007-2024年 具体看数据类型)

数据类型&#xff1a; 1.绿色债券数据&#xff1a;2014-2023 2.绿色信贷相关数据&#xff1a;2007-2022 3.全国各省及地级市绿色金融指数&#xff1a;1990-2022 4.碳排放权交易明细数据&#xff1a;2013-2024 5.绿色金融试点DID数据&#xff1a;2010-2023 数据来源&#…

RK3588 Android12实现UVC输出功能详解

首先需要在相关部分添加uvc的功能&#xff0c;这里参考一下&#xff1a;rockchip rk3588添加uvc及uvc,adb的复合设备_uvc.gs6-CSDN博客 setprop sys.usb.config none;setprop sys.usb.config uvc 或者setprop sys.usb.config none;setprop sys.usb.config uvc,adb 使rk3588 进…

为什么需要服务器?服务器可以做些什么

目录 一、服务器和电脑的区别二、什么是SSH三、什么是免密码登录四、服务器如何实现SSH免密码登录 一、服务器和电脑的区别 服务器和电脑是两种不同类型的计算机系统&#xff0c;它们在设计、功能和用途上存在明显的区别。首先&#xff0c;从硬件配置上看&#xff0c;服务器通…

基于java+springboot+vue实现的大学生就业需求分析系统(文末源码+Lw)233

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自…

基于java+springboot+vue实现的药店管理系统(文末源码+Lw)285

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;药品信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

【Linux】:程序地址空间

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关Linux程序地址空间的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从…

016-GeoGebra基础篇-加载项错误_使用此功能所需的服务已关闭,请检查你的隐私设置,

最近有伙伴说遇到一个问题&#xff1a;“加载项错误_使用此功能所需的服务已关闭&#xff0c;请检查你的隐私设置”&#xff0c;该怎么解决&#xff1f; 若大家也遇到同样的问题&#xff0c;建议按照我下边的步骤逐个排查下&#xff0c;基本可以解决“GeoGebra无法完美插入PPT…

利用border绘制三角技巧

绘制三角形的效果如图 <html lang"zh-cn"> <head><meta charset"UTF-8"><title>demo</title><style>* {margin: 0;padding: 0;}.box {/* 盒子宽高改成零就变成三角形 &#xff0c;需要哪个方向的三角形就设置哪个方向…

PD协议诱骗芯片,XSP08Q,XSP16应用笔记

XSP08Q是3C数码或小家电产品的Type-C接口控制芯片&#xff0c;它负责和PD充电器通讯&#xff0c;获取充电器的快充电压档位&#xff0c;如5V4A&#xff0c;9V3A&#xff0c;12V2A&#xff0c;15V3A&#xff0c;20V5A等等。 XSP08Q支持PD协议&#xff0c;BC1.2协议&#xff0c;Q…

Spring MVC 获取请求数据的四种方式,以及获取请求头数据,获取Cookie 的数据,设置Spring MVC 的字符集编码过滤器

1. Spring MVC 获取请求数据的四种方式&#xff0c;以及获取请求头数据&#xff0c;获取Cookie 的数据&#xff0c;设置Spring MVC 的字符集编码过滤器 文章目录 1. Spring MVC 获取请求数据的四种方式&#xff0c;以及获取请求头数据&#xff0c;获取Cookie 的数据&#xff0c…

【IT领域新生必看】Java中的Static关键字详解:小白也能轻松掌握的神奇用法

文章目录 引言什么是Static关键字&#xff1f;Static变量&#xff08;类变量&#xff09;定义和使用示例&#xff1a; 应用场景 Static方法&#xff08;类方法&#xff09;定义和使用示例&#xff1a; 应用场景 Static代码块定义和使用示例&#xff1a; 应用场景 Static嵌套类定…

ESP32 通过蓝牙显示歌词代码示例

通过蓝牙协议播放音乐&#xff0c;有的时候需要显示歌词&#xff0c;这里就是a2dp库获取了歌词 值得注意的是要想正确获取到歌词&#xff0c;必须打开各种播放器的字幕&#xff08;歌词&#xff09;开关 本项目用了三个开源库 a2dp&#xff0c;tft_espi,xfont. a2dp &#x…

Qt 网络编程 udp通信

学习目标&#xff1a;使用udp通信 前置环境 运行环境:qt creator 4.12 学习内容 UDP 协议基础知识 1、UDP(用户数据报协议)是轻量的、不可靠的、面向数据报、无连接的协议&#xff0c;用于可靠性要求不高的场合。两个应用程序之间进行UDP 通信不需先建立持久的 socket 连接…

【IT领域新生必看】解密Java中的静态方法与实例方法:小白也能轻松掌握的全方位指南

文章目录 引言什么是静态方法&#xff1f;定义和使用静态方法示例&#xff1a; 静态方法的特点示例&#xff1a; 什么是实例方法&#xff1f;定义和使用实例方法示例&#xff1a; 实例方法的特点示例&#xff1a; 静态方法与实例方法的区别作用范围示例&#xff1a; 访问权限示…

宁德时代天行发布,商用车超充时代来临

近日&#xff0c;宁德时代正式推出商用动力电池品牌——“宁德时代天行”&#xff0c;同时发布“宁德时代天行轻型商用车&#xff08;L&#xff09;-超充版”和“宁德时代天行轻型商用车&#xff08;L&#xff09;-长续航版”两款产品&#xff0c;可实现4C超充能力和500km的实况…

14-38 剑和诗人12 - RAG+ 思维链 ⇒ 检索增强思维(RAT)

在快速发展的 NLP 和 LLM 领域&#xff0c;研究人员不断探索新技术来增强这些模型的功能。其中一种备受关注的技术是检索增强生成 (RAG) 方法&#xff0c;它将 LLM 的生成能力与从外部来源检索相关信息的能力相结合。然而&#xff0c;最近一项名为检索增强思维 (RAT) 的创新通过…

计算机网络(2

计算机网络续 一. 网络编程 网络编程, 指网络上的主机, 通过不同的进程, 以编程的方式实现网络通信(或网络数据传输). 即便是同一个主机, 只要不同进程, 基于网络来传输数据, 也属于网络编程. 二. 网络编程套接字(socket) socket: 操作系统提供的网络编程的 API 称作 “soc…

免杀笔记 ----> DLL注入

这段时间我们暂时没什么事情干的话我们就继续更新我们的免杀笔记力&#xff01;&#xff01;&#xff01; &#xff1a;今天我们讲DLL注入 目录 1.DLL注入 2.直接加载DLL&#xff1f; 3.远程线程注入 获取Handle 远程申请内存空间 将我们的CS的DLL加载入内存 创建远程线…