赶紧进来看看---万字博客详解C/C++中的动态内存管理

news2024/11/21 1:26:36

本篇博客主要介绍了C/C++程序内部的内存开辟.动态内存分布 动态内存函数malloc calloc realloc free的使用 常见的动态内存错误.以及柔性数组的概念与使用
学会动态内存管理将不再局限于使用静态的空间,对内存空间的理解和使用将更进一层楼~

C/C++动态内存管理

  • 一.认识C/C++程序的内存开辟
  • 二.什么是动态内存
  • 三.为什么会存在动态内存分布
  • 四.动态内存函数的介绍
    • 1.malloc动态内存开辟函数
    • 2.calloc动态内存开辟函数
    • 3.realloc动态内存分配函数
    • 4.free释放动态内存空间函数
  • 五.常见的动态内存错误
    • 1.对free后指针的解引用操作
    • 2.对动态开辟空间的越界访问
    • 3.对非动态开辟内存使用free释放
    • 4.使用free释放一块动态开辟内存的一部分
    • 5.对同一块动态内存多次释放
    • 6.动态开辟内存忘记释放(内存泄漏)!!!
    • 7.在死循环里动态开辟内存空间
  • 六.柔性数组
    • 1.柔性数组的特点
    • 2.柔性数组的开辟方式
    • 3.柔性数组的使用
    • 4.柔性数组的优势
  • 七. 动态内存管理总结

一.认识C/C++程序的内存开辟

C/C++程序内存分配的几个区域:

  1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结 束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是 分配的内存容量有限。
    栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分 配方式类似于链表。
  3. 数据段(静态区static存放全局变量、静态数据。程序结束后由系统释放。
  4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码

在这里插入图片描述

实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。
但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序
结束才销毁
所以生命周期变长。

二.什么是动态内存

动态内存是相对静态内存而言的。所谓动态和静态就是指内存的分配方式。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存。

前面所写的程序大多数都是在栈上分配的,比如局部变量、形参、函数调用等。栈上分配的内存是由系统分配和释放的,空间有限,在复合语句或函数运行结束后就会被系统自动释放。而堆上分配的内存是由程序员通过编程自己手动分配和释放的,空间很大,存储自由。

三.为什么会存在动态内存分布

int a=10;  //在栈空间申请4字节空间
int arr[10]={0}; //在栈空间申请40个字节空间

我们现在已经掌握了在栈区申请开辟空间,
而在栈区申请空间有以下特点:
1.栈区开辟的空间即定义的变量是局部变量,出了所在的局部范围后生命周期会结束,在局部范围里时变量会一直存在…
2.栈区开辟的空间大小是固定的,一旦开辟后大小无法发生变化…
3.在栈区开辟数组空间,数组长度必须是确定的,在编译时为其分配固定长度的内存空间

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。这时候就只能试试动态存开辟了

四.动态内存函数的介绍

动态内存即可以使内存动态变化,当我们不确定数组长度具体是多少时 (数组长度小时,存在数据溢出不够存放的问题,而数组长度大时,存在占用可用空间太多的问题),
实际运行中很少能够准确使用到合适的数组长度,而用动态内存可以根据实际编译情况为数组动态开辟合适的空间
而开辟动态内存需要用到c语言提供的动态内存函数…
而使用这些库函数都需要包含头文件 #include<stdlib.h>

1.malloc动态内存开辟函数

malloc动态内存开辟函数右边是官方文档->malloc库函数

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己
来决定。
如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

在这里插入图片描述

malloc 返回类型是void *
形参类型是size_t
作用为,调用malloc函数时实参为一个具体整数n表示在堆区开辟n个字节的空间,开辟成功返回开辟的内存空间的起始地址,开辟失败返回一个NULL指针,(开辟失败可能存在堆区空间占满的情况)
返回的指针是空类型的,因为开辟空间时编译器不确定开辟后的空间是什么类型的,最后返回空类型指针
通过强制类型转换为对应类型的指针,让编译器知道你对开辟的空间以什么类型进行访问,可将空间按你强转的类型进行访问操作
而malloc开辟的空间开辟后空间里的值是不确定的,需要自己设置值

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int *p=(int*)malloc(40);
	if (p == NULL)
	{
		printf("开辟空间失败\n");
		return 1;
	}
	int arr[10];
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
		arr[i] = i;
		printf("%d %d\n", p[i],arr[i]);
		

	}
	return 0;
}
}

在这里插入图片描述

上面代码通过使用malloc在堆区开辟了40个字节空间(经过判断返回的指针是否为NULL如果是则表示开辟失败则结束程序),每4个内存单元为一个整形空间, 在栈区申请开辟了一个有10个元素的整形数组
二者的区别是一个在堆区开辟空间,一个在栈区开辟空间,本质上都是一段用于存放10个整形的地址连续的内存空间
最后通过循环输入值,输入值得出的结果都相同…

2.calloc动态内存开辟函数

右边是calloc库函数官方文档->calloc
calloc函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
与函数 malloc 的区别在于参数表示num个size字节空间而malloc参数为多大的字节空间
calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

在这里插入图片描述

calloc和malloc对比:
malloc 只有一个参数假如为40表示开辟40个字节空间
colloc有两个参数(10,4)表示开辟10个4字节的空间即10*4为40个字节空间,二者最后开辟的空间字节个数是一样的
malloc开辟的空间最后不会初始化 是随机值
calloc开辟的空间每个内存单元都会初始化为0…

#include<stdio.h>
#include<stdlib.h>
int main()
{//返回的是void* 可以强转类型后赋给对应类型变量也可以直接赋值最后都会根据变量的类型自身转换为该类型赋值
	int* p = calloc(10, sizeof(int));
	int* l = malloc(40);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
		
		printf("%d \n", *(l + i));
	}
	return 0;
}

在这里插入图片描述

可以看到calloc 和malloc都开辟了40个字节空间,最后都以整形输出10个整形元素 calloc里为10个0 而malloc是随机值

3.realloc动态内存分配函数

右边为realloc动态内存分配函数官方文档->realloc

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小
的调整。

在这里插入图片描述

realloc库函数返回类型是void*
第一个形式参数ptr为void*类型,接受的是要重新开辟的空间的起始地址
第二个参数是size_t 表示无符号整数 表示要重新开辟的空间大小
如果第一个参数接受的是NULL则表示在堆区开辟第二参数表示的字节个数的空间等同于malloc

realloc主要作用就是是原先开辟的空间动态减少或者动态增长,达到动态变化的效果
使原来已有的空间减少时传递的字节个数比原来空间小,此时会减少原来空间,减少的空间数据被丢失…最后返回当前被减少后的内存空间的指针…

在这里插入图片描述

realloc是原来空间增加传递的字节个数比原来空间大,此时有两种情况
当给当前空间增长时,后面能容纳增长的区域,直接在当前空间后面加要增长的字节空间个数,返回当前空间起始地址,即原空间起始地址
当给当前空间增长时.后面有其他空间存在或者后面已经到了堆区范围,此时会重新在堆区找一块能容纳增长后所有字节大小的区域开辟一块空间,将原来空间的数据拷贝到当前空间并释放掉原来空间,返回当前被开辟的空间起始地址

在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)realloc(NULL, 20);
	if (p == NULL)
	{
		printf("开辟失败\n");
			return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		p[i] = i;
		printf("%d ", p[i]);
	}
	printf("\n");
	p = (int*)realloc(p, 12);   //重新开辟12个字节空间 表示在原来空间上减少后面8字节空间
	for (i = 0; i < 3; i++)
	{
		p[i] = i;
		printf("%d ", p[i]);
	}
	printf("\n");
	int* tmp = (int*)realloc(p, 40);  //重新开辟40个字节空间,在原来空间基础上动态增长20个字节空间可能开辟失败会返回NULL ,先用一个临时变量接受
	if (tmp == NULL)
	{
		printf("增容失败\n");
		return 1;
	}
	p = tmp;
	for (i = 0; i < 10; i++)
	{
		p[i] = i;
		printf("%d ", p[i]);
	}
	return 0;
}

在这里插入图片描述

上面为测试realloc一个参数为NULL指针时,相当于malloc函数开辟后面20个字节空间
将返回的起始地址转换为整形指针,即将20个字节空间看成5个整形空间,分布赋值后输出
用realloc(p,12)表示重新开辟12个字节,等价于将p指针指向的动态内存空间动态减少后面8字节空间,得到12个字节空间,后面8字节空间数据丢失,前面的仍保留,实现了动态减少的效果

realloc(p,40)表示重新开辟40个字节空间,等价于都p指针指向的动态内存空间往后增长到40个字节空间,可能出现增长时容量已满增容失败返回NULL
此时先用一个临时指针变量接受返回的指针,经过判断不是NULL指针后将其赋值给p
此时p指针得到的是指向40个字节空间的指针,增长前空间的数据仍然会被保留,实现了动态增长的效果

4.free释放动态内存空间函数

右边数free库函数的官方文档->free

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

在这里插入图片描述

free 形参类型为void* 表示接受的是一个指向需要释放的动态空间的指针,
返回类型是void表示无返回值

free是一个很重要的函数, 我们知道在栈区申请的局部空间,在出其局部范围时空间会被释放,
而堆区申请的空间,只有在整个程序结束时才会被释放,而往往有时候我们只需要临时开辟空间,用完就不再使用了,而不使用这个空间但是仍然在程序里就会浪费不必要的空间
需要做到在程序运行时释放空间,就要用到free函数

#include<stdio.h>
#include<stdlib.h>
void print()
{
	int* p = (int*)malloc(40);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		p[i] = i;
		printf("%d ", p[i]);	
	}
}
int main()
{
	
	print();
	return 0;
}

当我们在一个自定义函数里使用动态内存函数在堆区申请空间进行某些操作时,如上面代码使用malloc在堆区申请40个字节空间然后当做十个整形元素赋值并遍历打印
执行完这个函数后,显然这些空间我们已经不需要再使用了,而它并不会像数组一样出了这个函数空间被释放

堆区申请的空间要在整个程序结束后才会被自动释放还给操作系统,在上面代码运行完函数后最后结束程序会释放空间
但实际以后编写的程序都是在服务器之类上运行,而服务器是二十四小时不间断运行的,不会结束程序,而随着程序运行实现某些功能不停在堆区申请空间而释放不了,最后会造成空间浪费很多会导致服务器崩溃

要使程序在运行过程中释放已经申请开辟了的且不需要使用的堆区空间就需要用到free函数

#include<stdio.h>
#include<stdlib.h>
void print()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");   //perror打印错误信息函数 
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		p[i] = i;
		printf("%d ", p[i]);
	}
	free(p);    //p为指向堆区开辟的空间的起始地址 传参调用free函数释放这个开辟的空间
	p = NULL;   //释放后p里的指针指向的未知空间是野指针 为了避免对野指针解引用将p置为NULL
}
int main()
{
	
	print();
	return 0;
}

上面代码加了free函数,参数是指向堆区申请开辟的空间的起始地址,作用是将传递的指针指向堆区的空间释放还给操作系统,此时就实现了在程序运行中释放掉堆区申请开辟的空间
注意:p指向的已开辟堆区空间被释放后,p指针此时指向的一块未知空间是一个野指针,为了避免再对p野指针解引用操作将p置为NULL

五.常见的动态内存错误

在堆区申请开辟动态内存比栈区的静态内存更为灵活,但是使用它需要更谨慎,使用不当会出现很多内存错误,要合理使用并管理动态内存,得认识常见的动态内存错误

1.对free后指针的解引用操作

void test()
{
int *p = (int *)malloc(INT_MAX/4);
free(p);  //free后p指向的空间被释放

*p = 20;//此时p指针属于野指针,解引用赋值就会有问题

申请一块动态内存得到指向该内存的指针 后,通过指针free释放掉这个内存空间后,这个指针变为了野指针,此时不小心再对野指针解引用操作是错误的…
free后不能再对free后的空间的指针进行解引用操作

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

void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
   perror("test");
   return 1;
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}

上面代码开辟了40个字节的空间,分为10个4字节的整形空间访问,但通过循环访问时i为10访问到了第11个整形空间,属于越界访问,是错误的…
访问开辟的空间要注意不要发生越界访问

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

void test()
{
int a = 10;
int *p = &a;
free(p);//ok?

上面代码 在栈区创建了一个整形空间 ,取到整形空间地址给了整形变量p ,通过传p给free释放p指向的空间,而p指向的不是堆区的动态空间,会出错…
不能对非动态内存空间进行free

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

void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}

上面代码开辟了100个字节的空间,返回起始地址给了p, 运行了p++,此时p指针是指向malloc开辟的空间第二个整形位置,即变为了偏移量4的地址, 将p传给free表示从当前偏移量4的位置往后释放空间,而前面4个字节的空间未被释放,是错误的…
不能free动态内存的一部分,要从起始地址开始往后free

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

void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}

上面代码 开辟了100个字节空间 ,返回起始地址给了p,然后free§将开辟的空间释放后,又运行了free§此时p属于野指针,再次free是错误的…
不能对同一块动态内存空间多次释放

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

void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}

上面代码在test函数里动态开辟了100个字节的空间,对该空间进行访问操作后结束函数,
进入了一个死循环,这个死循环就类比服务器程序,当一个程序不间断运行时,而之前开辟的动态内存因为使用完后没有及时free掉,结束函数后记录该空间的指针变量也以为函数结束而被释放,此时再也无法找到指向该动态内存的指针,而该内存也没有被释放,将一直残留在程序里也使用不了,造成严重错误,这个错误也被称作内存泄漏
动态开辟的空间一定及时free

7.在死循环里动态开辟内存空间

#include<stdio.h>
int main()
{
	while (1)
	{
		malloc(10);
	}
	return 0;
}

当我们在一个循环里,动态开辟空间,随着循环次数越多,也就可能导致运行开辟动态内存空间函数越多,在堆区开辟的空间也就越多,此时还没有对其free的话,很有可能使电脑内存占满而崩溃!!!
上面代码写的一个死循环,下面看看在死循环里不断动态开辟内存里是什么情况

在这里插入图片描述

上面是没有运行时的截图,先打开任务管理器ALT+Ctrl+Delete查看内存情况是正常的
下面冒着死机风险运行了程序

在这里插入图片描述

可以看到才运行了一会儿,电脑内存就已经使用率到了94%!!!,可以看到,不free空间,又频繁动态开辟空间的后果,最后可能会导致电脑死机!!

六.柔性数组

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

1.柔性数组的特点

结构体中的柔性数组成员前面必须至少一个其他成员。
sizeof 返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

2.柔性数组的开辟方式

typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;

有些编译器会报错则可以改成:

typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;

根据柔性数组特点可以看出这种开辟方法一开始数组是没有元素个数即的,使用sizeof 求出来的也是柔性数组以外的成员大小

#include<stdio.h>
struct S
{
	int i;
	int arr[];
};
int main()
{
	printf("%d", sizeof(struct S));//输出结果是什么
	return 0;
}

根据特点,结构体最后一个成员变量为int arr[]未指定数组元素个数是一个柔性数组,此时求struct S的大小是除柔性数组以外的其他成员变量(再考虑内存对齐),最后结构大小为4字节

在这里插入图片描述

3.柔性数组的使用

柔性数组是没有元素个数的,而它的特性就是可以通过动态内存函数使其类似于获得自身的元素个数即内存空间,也可以通过重置动态内存函数改变其后面访问元素个数,实现数组空间动态增长!!!

#include<stdio.h>
#include<stdlib.h>
struct S
{
	int i;
	int arr[];
};
int main()
{
	struct S *p=(struct S*)malloc(sizeof(struct S) + sizeof(int) * 10);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	printf("%d ", sizeof(*p));//输出结果是什么
	p->i = 10;
	int n = 0;
	for (n = 0; n < 10; n++)
	{
		p->arr[n] = n;
		printf("%d ", p->arr[n]);
	}
	free(p);
	p = NULL;
	return 0;
}

上面代码使用了柔性数组,最后输出结果为4 0 1 2 3 4 5 6 7 8 9
先使用malloc开辟了(4+40)个空间,开辟成功后返回起始地址,将地址转换完struct S * 结构体类型指针,而这个结构体里有int 类型和柔性数组成员变量,因为柔性数组开始大小为0,所以开始的struct S* 指针是指向动态内存空间起始4个字节的整形空间的指针,将其赋值给结构体指针变量p
对其解引用访问了还是4个字节整形空间,sizeof求类型大小结果为4

前面四个字节的整形空间是struct s的也就是第一个成员变量int a的,后面开辟40个字节空间并不会浪费掉,这些空间是属于能被柔性数组int arr[]访问的空间,虽然柔性数组不算类型字节但是可以看做是一个指针访问后面的空间 即40个字节为10个整形空间类似于分配给柔性数组的空间即给了柔性数组10个元素,通过arr对应下标可以访问这十个整形空间

柔性数组没有指定元素个数但是在后面有可用的空间时可以根据数组类型将后面开辟的空间当成内部元素访问后面的空间

在这里插入图片描述

4.柔性数组的优势

上面代码中的整形柔性数组就类似于一个整形指针,同样也可以写成下面这种形式

#include<stdio.h>
#include<stdlib.h>
struct S
{
	int a;
	int* arr;  //整形指针变量代替柔性数组
};

int main()
{
	struct S* p = (struct S*)malloc(sizeof(struct S));
	p->a = 10;
	p->arr = (int*)malloc(40);
	if (p->arr == NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		p->arr[i] = i;
		printf("%d ", p->arr[i]);
	}
   //释放空间
	free(p->arr);
	p->arr=NULL;
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

可以看到最后代码输出结果是一样的,虽然这种写法和柔性数组实现效果一样,但是柔性数组的实现有两个好处

好处一:

可以看到代码一malloc开辟了两次内存空间,最后释放了两次空间
而柔性数组只需要开辟一次使用完后也只释放一次

如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。
用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
因此使用柔性数组动态内存开辟和使用后的释放更方便

好处二:

连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正你少不了要用做偏移量的加法来寻址)
使用柔性数组有益于提高访问速度

七. 动态内存管理总结

本文主要介绍了动态内存函数malloc calloc realloc free 以及使用动态内存函数需要了解和避免出现的常见错误和柔性数组的概念与使用,
先以熟悉为主 学会使用动态内存,对内存空间的掌握将更进一步,不再局限于静态内存的使用,
具体的代码实操练习在下一篇博客:用动态内存将静态学生信息管理改变为动态内存增长版…

在这里插入图片描述

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

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

相关文章

【C++升级之路】类与对象(中)

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【C学习与应用】 ✒️✒️本篇内容&#xff1a;类与对象知识汇总&#xff0c;包括6大默认成员函数、日期类的实现 &#x1f6a2;&#x1f6a2;作者简介&…

graphQL入门分享

是什么 一种用于 API 的查询语言&#xff1b;它与特定技术无关&#xff0c;你可以用任何语言实现它 简单理解&#xff0c;他能提供一个接口&#xff0c;让我们来调用&#xff0c;只是返回的数据格式更多是由我们前端来控制 为什么 官网&#xff1a;https://graphql.cn/ 1.请求你…

深入理解计算机系统前篇总结

&#x1f343;博主昵称&#xff1a;一拳必胜客 博主主页面链接&#xff1a;博主主页传送门 博主专栏页面连接&#xff1a;专栏传送门–计算机考研 &#x1f351;创作初心&#xff1a;本博客的初心是每天分享记录自己学习的脚步&#xff0c;和各位技术友探讨交流&#xff0c;同时…

惠州龙门大米飘香 国稻种芯-中国水稻节:广东乡村振兴样板

惠州龙门大米飘香 国稻种芯-中国水稻节&#xff1a;广东乡村振兴样板 人民日报客户端 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道&#xff1a; 粒粒“龙门大米”精美飘…

百度地图API的使用(附案例)

文章目录JavaScript API GL一、申请秘钥Hello World显示地址案例定位功能步行导航搜索功能地铁路线规划JavaScript API GL 百度地图JavaScript API是一套由JavaScript语言编写的应用程序接口&#xff0c;可帮助您在网站中构建功能丰富、交互性强的地图应用&#xff0c;支持PC端…

经典文献阅读之--用于自动驾驶的高清地图生成技术

0. 简介 这篇文章我们介绍一下论文“High-Definition Map Generation Technologies For Autonomous Driving: A Review“&#xff0c;2022年6月11日&#xff0c;来自加拿大Ontario Tech University。相较于网上的其他文章&#xff0c;本文更在意向读者更轻松全面的了解文章中介…

【Web前端大作业】基于HTML+CSS+JavaScript制作西北大学新闻网站(7页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

悬浮坐标解决方案:如何在图片获取xy鼠标位置和增加标注信息

悬浮坐标的定义&#xff0c;基于固定分辨率的图片&#xff0c;通过获取该图片x和y坐标确定位置后并添加标注&#xff0c;实现位置展示、对应图片内物品展示的一种标注开发方式。 技术要点 自动获取图片x和y坐标&#xff1b;将多个坐标xy在图片上通过CSS定位的方式予以展示&am…

pytorch学习(三)——模型层

文章目录1. 自定义模型层2. 使用预训练模型3. 模型构建风格3.1 使用 add_module 方法3.2 添加进 Sequential3.3 Sequential作为模型容器3.4 ModuleList作为模型容器3.5 ModuleDict作为模型容器当我们构建了数据管道能够将数据一个batch一个batch的取出来后&#xff0c;下一步就…

微信小程序函数处理之保姆级讲解

目录 生命周期函数 生命周期函数的调用过程 页面事件函数 页面路由管理 自定义函数 setData设值函数 生命周期函数 在使用Page&#xff08;&#xff09;构造器注册页面时&#xff0c;需要使用生命周期函数&#xff0c;包括onLoad&#xff08;&#xff09;页面加载时生命周…

硬件工程师成长之路(10.1)——芯片选型

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录前言一、电机驱动类1 、直流电机驱动芯片2、步进电机③、资料前言 送给大学毕业后找不到奋斗方向的你&#xff08;每周…

【车间调度】基于全球邻域和爬坡来优化模糊柔性作业车间调度问题(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…

VS Studio 搭建跨平台开发环境

VS Studio 搭建跨平台开发环境 增加VS的工作负载 打开Visual Studio Installer 安装器&#xff0c;点击修改 在这个界面找到Linux开发环境&#xff0c;勾上然后在点击右下角的修改等待安装。我的是因为已经有了所以下面那里显示的是关闭&#xff0c;没有的是显示的修改 等待安…

LabVIEW强制重新安装无法运行或损坏的NI软件

LabVIEW强制重新安装无法运行或损坏的NI软件 可以参考附件的录像说明。LabVIEW强制重新安装无法运行或损坏的NI软件 - 北京瀚文网星科技有限公司 (bjcyck.com) 某些NI软件&#xff0c;工具包或驱动程序已损坏&#xff0c;损坏或无法按预期运行&#xff0c;想尝试重新安装以进…

【ArchSummit】众安金融微服务架构演进实战

前言 &#x1f4eb; 作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &a…

网络原理——传输层_UDP

JavaEE传送门JavaEE JavaEE——No.2 套接字编程(TCP) JavaEE——网络原理_应用层 目录传输层UDP传输层 端到端之间的传输, 重点关注的是起点和终点 核心的协议有两个: UDP: 无连接, 不可靠传输,面向数据报, 全双工 TCP: 有链接, 可靠传输, 面向字节流, 全双工 UDP UDP协议…

nginx+tomcat(二)

四层代理: 四层代理: 一般使用七层代理也就是http应用层代理&#xff0c;可以反向代理和负载均衡。但是项目要使用长连接&#xff0c;此时内网服务器肯定不能暴漏&#xff0c;还是需要接入层网关进行转发&#xff0c;一般有使用lvs&#xff0c;lvs专门用作四层代理和负载均衡基…

【C++】模板初阶

文章目录一、泛型编程二、函数模板1、概念与格式2、底层原理3、实例化4、参数的匹配规则三、类模板1、概念与格式2、实例化一、泛型编程 我们通过实现一个通用的交换函数来引入泛型编程&#xff1a; void Swap(int& left, int& right) {int temp left;left right;r…

Linux 命令(147) —— truncate 命令

文章目录1.命令简介2.命令格式3.选项说明4.常用示例参考文献1.命令简介 truncate 将文件的大小缩小或扩展到指定的大小。 如果指定的文件不存在将被创建。 如果文件大于指定的大小&#xff0c;则会丢失额外的数据。如果较短&#xff0c;它将被扩展&#xff0c;扩展的稀疏部分…

【牛客刷题--SQL篇】多表查询组合查询SQL25 查找山东大学或者性别为男生的信息

&#x1f496;个人主页&#xff1a;与自己作战 &#x1f4af;作者简介&#xff1a;CSDN博客专家、CSDN大数据领域优质创作者、CSDN内容合伙人、阿里云专家博主 &#x1f49e;牛客刷题系列篇&#xff1a;【SQL篇】】【Python篇】【Java篇】 &#x1f4cc;推荐刷题网站注册地址&a…