【ONE·C || 动态内存管理】

news2025/1/16 13:56:56

总言

  C语言:动态内存管理介绍。

文章目录

  • 总言
  • 1、为什么存在动态内存管理
  • 2、动态内存函数介绍
    • 2.1、malloc、free
      • 2.1.1、malloc函数
      • 2.1.2、free函数
    • 2.2、calloc、realloc
      • 2.2.1、calloc函数
      • 2.2.2、realloc函数
  • 3、常见的动态内存错误
    • 3.1、对NULL指针的解引用操作
    • 3.2、对动态开辟空间的越界访问
    • 3.3、对非动态开辟内存使用free释放
    • 3.4、使用free释放一块动态开辟内存的一部分
    • 3.5、对同一块动态内存多次释放
    • 3.6、动态开辟内存忘记释放(内存泄漏)
  • 4、习题演练
    • 4.1、题一
    • 4.2、题二·返回栈空间地址、返回栈空间变量
    • 4.3、题三
    • 4.4、题四
  • 5、柔性数组
    • 5.1、什么是柔性数组
    • 5.2、柔性数组的特点和使用
      • 5.2.1、柔性数组的特点
      • 5.2.2、柔性数组的使用
    • 5.3、柔性数组的优势
      • 5.3.1、一个对比
      • 5.3.2、柔性数组的优势

  
  

1、为什么存在动态内存管理

  1)、内存开辟方式:
  下示为常见变量开辟方式:

int n = 2;//全局变量:在静态区开辟四个字节

int main()
{
	int val = 20;//局部变量:在栈空间上开辟四个字节
	char arr[10] = { 0 };//局部变量:在栈空间上开辟10个字节的连续空间
	return 0;
}

  对于上述变量,可以知道的是: 这些变量开辟出的空间大小是固定的。即使是数组,为了在编译时确定其所需要的内存,也会固定数组长度。
  这样就存在一个问题,有时候我们需要的内存空间大小只有在程序运行的时候才能知道,直接固定内存大小,会遇到匹配不当的现象,比如内存空间过大浪费,或内存空间过小不够。
  因此才有了即将了解的动态内存开辟。
  
  
  2)、内存空间区域分配简单引入:
在这里插入图片描述

  
  
  

2、动态内存函数介绍

在这里插入图片描述

2.1、malloc、free

2.1.1、malloc函数

  1)、malloc函数介绍:

  相关函数:malloc
在这里插入图片描述
  说明:
  1、这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
  2、如果开辟成功,则返回一个指向开辟好空间的指针。如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  3、返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  4、如果参数 size0malloc的行为是标准是未定义的,取决于编译器。
  
  
  2)、使用演示:

#include<stdlib.h>

int main()
{
	int* ptr = (int*)malloc(40);//注意此处40的单位是字节,类型为int*,那么指针每次能访问4字节空间。
	if (ptr == NULL)//因为有申请失败的可能性,因此需要做检查。
	{
		perror("malloc");
		return 1;
	}
	//申请到的动态内存空间的相关使用举例:
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	return 0;
}

在这里插入图片描述

  
  

2.1.2、free函数

  1)、free函数介绍:

  相关函数链接:free
在这里插入图片描述

  说明:
  1、函数free是用来释放和回收态内存的。
  2、如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  3、如果参数 ptrNULL指针,则函数什么事都不做。

	int* np = NULL;
	free(np);//合法的,free参数可为空

  4、当我们不释放动态申请的内存空间时,如果程序结束,动态申请的内存由操作系统自动回收,如果程序不结束,动态内存是不会自动回收的,就会形成内存泄漏的问题。
  
  
  2)、使用演示:

#include<stdlib.h>

int main()
{
	int* ptr = (int*)malloc(40);//注意此处40的单位是字节,类型为int*,那么指针每次能访问4字节空间。
	int* p = ptr;
	if (ptr == NULL)//因为有申请失败的可能性,因此需要做检查。
	{
		perror("malloc");
		return 1;
	}
	//申请到的动态内存空间的相关使用举例:
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	//释放空间
	free(p);
	p = NULL;
	return 0;
}

  注意事项:
  1、动态申请空间后,不需要时要记得使用free函数释放;
  2、free(p);实际释放的是p指针指向的那块动态内存空间,即将内存使用权收回,但实际上p仍旧指向该内存空间,ptr虽然释放了对应内存空间,但它还能指向该地址空间,即p成为了野指针,因此需要为其赋值为空。
在这里插入图片描述

  
  
  3)、动态内存申请失败举例:

#include<stdlib.h>

int main()
{
	int* ptr = (int*)malloc(INT_MAX);//申请整型最大值这么多的内存空间
	int* p = ptr;
	if (ptr == NULL)//检查。
	{
		perror("malloc");
		return 1;
	}
	//申请到的动态内存空间的相关使用举例:
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ptr + i) = i;
	}
	//释放空间
	free(p);
	p = NULL;

	return 0;
}

在这里插入图片描述

  
  
  

2.2、calloc、realloc

2.2.1、calloc函数

  1)、calloc函数介绍:

  相关函数链接:calloc
在这里插入图片描述

  说明:
  1、colloc函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0
  2、与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
  
  
  2)、使用演示:

int main()
{
	//等价空间大小:
	//int* ptr = (int*)malloc(40);
	//int* ptr = (int*)malloc(sizeof(int)*10);
	int* ptr = (int*)calloc(10, sizeof(int));//区别在于初始化为零

	if (ptr == NULL)//检查
	{
		perror("malloc");
		return 1;
	}

	//使用举例:
	int* p = ptr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}

	//释放空间
	free(ptr);
	ptr = NULL;

	return 0;
}

在这里插入图片描述

  
  
  

2.2.2、realloc函数

  相关函数链接:realloc
在这里插入图片描述

  说明:
  1、有时为了合理设计内存空间,我们会对内存的大小做灵活的调整(申请的空间太小、太大)。那么 realloc 函数就可以做到对动态开辟内存大小的调整。
  2、realloc函数的两个参数,ptr 是要调整的内存地址,size 是调整之后新大小。
  3、返回值为调整之后的内存起始位置。根据不同情况,该函数会在调整原内存空间大小的基础上,将原来内存中的数据移动到新的空间。

  4、关于扩容时新空间选择说明:
  情形一: 当内存空间足够,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
在这里插入图片描述
  
  情形二: 原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。
  
在这里插入图片描述

  
  
  2)、使用演示:

int main()
{
	//预备处理:
	int* ptr = (int*)calloc(10, sizeof(int));
	if (ptr == NULL)//检查
	{
		perror("malloc");
		return 1;
	}
	printf("%p: ", ptr);

	//使用举例:
	int* p = ptr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}


	//重新扩容:
	int* tmp = (int*)realloc(ptr, 20 * sizeof(int));
	if (tmp != NULL)//检查:防止出现扩容失败,直接赋值的话会导致原先ptr改变指向引起内存泄漏
	{
		ptr = tmp;
	}
	printf("\n%p: ", tmp);

	//使用举例:
	p = ptr;
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", p[i]);
	}

	//释放空间
	free(ptr);
	ptr = NULL;

	return 0;
}

在这里插入图片描述

  
  
  
  

3、常见的动态内存错误

3.1、对NULL指针的解引用操作

	int* p = (int*)malloc(1000);
	for (int i = 0; i < 250; i++)
	{
		p[i] = i;//error
		printf("%d ", *(p+i));//error
	}

  存在问题:若动态申请空间失败,返回空指针NULL,*NULL是非法的。
  
  解决方案:对动态申请后的相关返回值进行判空。

	int* p = (int*)malloc(1000);
	if (p == NULL)
	{
		//……
		return 1;
	}
	for (int i = 0; i < 250; i++)
	{
		p[i] = i;
		printf("%d ", *(p+i));
	}

  
  

3.2、对动态开辟空间的越界访问

	int* p = (int*)malloc(1000);
	if (p == NULL)
	{
		//……
		return 1;
	}
	for (int i = 0; i <=250; i++)//error
	{
		p[i] = i;
		printf("%d ", p[i]);
	}

  存在问题:i <=250实际越界。

  解决方案:对内存边界主动检查。

	int* p = (int*)malloc(1000);
	if (p == NULL)
	{
		//……
		return 1;
	}
	for (int i = 0; i <250; i++)
	{
		p[i] = i;
		printf("%d ", p[i]);
	}

  
  

3.3、对非动态开辟内存使用free释放

	int a = 10;
	int* p = &a;
	free(p);
	p = NULL;

在这里插入图片描述

  
  
  
  

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

int main()
{
	int* p = (int*)malloc(100);
	if (p == NULL)
	{
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = i;
		p++;
	}
	//释放空间
	free(p);//error
	p = NULL;

	return 0;
}

  存在问题:
在这里插入图片描述
  
  

3.5、对同一块动态内存多次释放

int main()
{
	int* p = malloc(100);
	if (p == NULL)
		return 1;
	
	free(p);
	//....
	free(p);//error

	p = NULL;

	return 0;
}

在这里插入图片描述

  
  

3.6、动态开辟内存忘记释放(内存泄漏)

void test()
{
	int* p = malloc(100);
	//……
}

int main()
{
	test();
	//.....
	while (1)
	{
		;
	}

	return 0;
}

  存在问题:test函数结束,栈销毁,局部变量p被销毁,没有释放动态空间,会导致内存泄露。

  解决方案:动态开辟的空间一定要释放,并且正确释放。

void test()
{
	int* p = malloc(100);
	//……
	free(p);
	p = NULL;
}

int main()
{
	test();
	//.....
	while (1)
	{
		;
	}

	return 0;
}

  
  
  

4、习题演练

4.1、题一

  问:请问运行Test 函数会有什么样的结果?

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

  问题分析:

void GetMemory(char* p)//问题一
{
	p = (char*)malloc(100);//问题二
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);//问题一
	strcpy(str, "hello world");//问题三
	printf(str);
}

  
  1、GetMemory(str);char* p 尽管str是指针,但指针变量仍旧属于变量的范畴,在函数传参时,对该指针变量而言这种写法属于值传递,除非说传递它的地址,即二级指指针(&str),这样才是址传递。
  2、p = (char*)malloc(100); 因值传递,临时变量p出了函数被销毁,在GetMemory中申请到的动态空间由于没有指向它的指针,最终无法找到,也无法释放,形成内存泄漏。
  3、strcpy(str, "hello world"); 因上述问题导致str指针未做任何改变,仍旧是空指针,对空指针的解引用会导致程序崩溃。
  
  延伸:printf(str);,这种写法正确吗?

int main()
{
	char* p = "Why We Sleep\n";//该指针指向字符首元素地址
	printf("Why We Sleep\n");

	char arr[] = "Why We Sleep\n";//数组名表示数组首元素地址
	printf(arr);
	//printf打印字符串,只需要传递首元素地址
	return 0;
}

在这里插入图片描述

  
  解决方案:

void GetMemory(char** p)
{
	*p = (char*)malloc(100);
}

void Test(void)
{
	char* str = NULL;
	GetMemory(&str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}

在这里插入图片描述

  
  
  

4.2、题二·返回栈空间地址、返回栈空间变量

  1)、基本例题说明:
  问:请问运行Test 函数会有什么样的结果?

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

  
  问题分析:在GetMemory函数中创建数组并将其地址返回给str指针。由于GetMemory函数结束后,其内部局部变量销毁,对应内存空间又还给系统本身,str得到的是一个没有访问权限的地址,则属于野指针。对其打印会出现未定义行为。
在这里插入图片描述

  
  
  2)、返回栈空间地址讲解:

int* test()
{
	int a = 10;
	return &a;
}

int main()
{
	int* p = test();

	printf("%d\n", *p);
	printf("%d\n", *p);

	return 0;
}

  第一次printf能打印成功有时属于侥幸,对应的空间中数据还没有被清理/删除/覆盖,当打印第二次时就能发现问题,这样因为相同空间被其它函数栈帧使用,故其中数据也不复从前。
  需要注意,即使第一次输出结果正确,但不代表它没问题,本质上这种写法就是非法的。举例子:偷窃,虽然结果是没被抓/发现,但这一行为本身不符立定的道德规范。
在这里插入图片描述  
  需要区分:返回栈空间的地址是有问题的,但返回栈空间的变量是可行的

int* test()//error:返回栈空间地址
{
	int a = 10;//创建一个int变量
	return &a;//返回的是该变量的地址:传址返回
}
int test()//right:返回栈空间变量
{
	int a = 10;
	int*p=&a;
	return *p;
}
int main()
{
	int n=test();
	return 0;
}

  
  延伸问题:
  如下:此提错误之处是访问野指针。非返回栈空间地址。

int* f2(void)
{
	int* ptr;//创建一个指针变量
	*ptr = 10;//error
	return ptr;//返回的是该指针变量,传值返回
}

  
  

4.3、题三

  问:请问运行Test 函数会有什么样的结果?

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

  
  问题分析:
  1、该代码能正常打印值。
  2、但没有释放动态申请的空间。
  3、如果要求严格一些,存在的瑕疵是没有判空。

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;
}

  
  
  

4.4、题四

  问:请问运行Test 函数会有什么样的结果?

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
int main()
{
	Test();
	return 0;
}

  
  问题分析:
  1、瑕疵:没有进行判空操作,当malloc申请失败时,第一次字符拷贝传参可能存在问题。
  2、问题:free释放动态内存空间,str仍然指向对应地址,只是该地址被系统收回,后续使用时str为野指针,虽能访问该内存空间,但其操作为非法访问。

  正确写法应在释放内存后令str为空指针。此处的一种错误改法是把free(str)放到if循环之后,这样虽然没有野指针的问题,但其逻辑不能自洽(即先用了strcpy拷贝,再用if语句判断:既然已经投入使用又何必多此一举添个判空,既然判空为什么不在刚完成空间申请后立马做判空检查)

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	str = NULL;//修改
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

  
  
  
  

5、柔性数组

5.1、什么是柔性数组

  C99 中,结构体最后一个元素允许是未知大小的数组,这就叫做柔性数组成员
  以下为结构体中,柔性数组的两种写法:

struct S1
{
	int num;
	double d;
	int arr[];//柔性数组成员
};


struct S2
{
	int num;
	double d;
	int arr[0];//柔性数组成员
	//此处的0不是指数组元素,而是特指柔性数组
};

  
  

5.2、柔性数组的特点和使用

5.2.1、柔性数组的特点

struct S1
{
	int num;
	int arr[0];//柔性数组成员
};

  1、结构中的柔性数组成员前面必须至少一个其他成员。
  2、sizeof 返回的这种结构大小不包括柔性数组的内存。
  
  演示如下:
  sizeof计算结构体,不包含柔性数组的大小,正因此,为了保障结构体大小存在,柔性数组不能单独存在结构体中。
在这里插入图片描述

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

	struct S1* ps1 = (struct S1*)malloc(sizeof(struct S1));//error
	struct S1* ps2 = (struct S1*)malloc(sizeof(struct S1) + 40);

  这种写法错误是因为我们只考虑了num的空间,正确写法是要加上柔性数组需要的空间大小。
  
  
  

5.2.2、柔性数组的使用

int main()
{
	struct S1* ps = (struct S1*)malloc(sizeof(struct S1)+40);
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}

	ps->num = 100;

	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i;//柔性数组成员:赋值
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", ps->arr[i]);//柔性数组成员:打印
	}
	printf("\n");

	//扩容
	struct S1* ptr = (struct S1*)realloc(ps, sizeof(struct S1)+80);
	if (ptr == NULL)
	{
		perror("realloc\n");
		return 1;
	}
	else
	{
		ps = ptr;
	}

	for (i = 10; i < 20; i++)//为后面扩容的值初始化
	{
		ps->arr[i] = i;
	}

	for (i = 0; i < 20; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");

	//释放
	free(ps);
	ps = NULL;

	return 0;
}

在这里插入图片描述
  
  
  

5.3、柔性数组的优势

5.3.1、一个对比

  以下为两种结构体数组的写法:
  使用柔性数组:

struct S1
{
	int num;
	int arr[0];//柔性数组成员
};

int main()
{
	struct S1* ps = (struct S1*)malloc(sizeof(struct S1)+40);
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}

	//使用
	//……


	//释放
	free(ps);
	ps = NULL;

	return 0;
}

  使用指针:

struct S2
{
	int num;
	int* arr;//指针 
};

int main()
{
	struct S2* ps = (struct S2*)malloc(sizeof(struct S2));
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	ps->arr = (int*)malloc(sizeof(int) * 10);
	if (ps->arr == NULL)
	{
		perror("malloc");
		free(ps);
		ps = NULL;
		return 1;
	}

	//使用
	//……

	//释放
	free(ps->arr);
	free(ps);

	return 0;
}

  
  可以看到,上述两种写法都能达到相同效果,那么我们为什么不直接使用指针,而要再学习一个柔性数组呢?
  以下为二者的区别:
在这里插入图片描述
  对第一种,数组是结构体中的一员,内存空间相衔接。
  对第二种,可取代柔性数组得到相同结果,但其内存空间是单独开辟的,存在不衔接的情况。

  
  

5.3.2、柔性数组的优势

  第一个好处是:方便内存释放
  如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好
了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
  第二个好处是:这样有利于访问速度.
  连续的内存有益于提高访问速度,也有益于减少内存碎片。

在这里插入图片描述

  
  
  
  
  
  
  
  

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

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

相关文章

TEX:显示文本

文章目录字体选择字体fontspec宏包根据字体形状控制字体为不同的字体形状选择不同的特征为不同的字体大小状选择不同的特征中文字体选择xeCJK宏包字体选择与设置XELATEX字体名查找字体集与符号居中与缩进居中单边调整两边缩进诗歌缩进列表itemize样例enumerate样例description样…

Java多线程(三)——线程池及定时器

线程池就是一个可以复用线程的技术。前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理&#xff0c;下次新任务来了又要创建新线程&#xff0c;而创建新线程的开销是很大的&#xff0c;这样会严重影响系统的性能。线程池就相当于预先创建好几个线程&#xf…

concrt140.dll丢失四种方法解决丨提示游戏里找不到concrt140.dll?

电脑提示concrt140.dll文件丢失怎么办&#xff1f;由于找不到concrt140.dll&#xff0c;无法继续执行代码&#xff1f; 我们平时在打开 Adobe 应用程序、Halo、Forza Horizon 5 地平线5 等时&#xff0c;可能会遇到找不到 concrt140.dll。因此&#xff0c;这不是特定于某个应用…

基频的后处理

基频归一化 基频为什么要归一化&#xff1f;为了消除人际随机差异&#xff0c;提取恒定参数&#xff0c;在语际变异中找到共性。 引言 声调的主要载体就是基频。但是对声调的感知会因人而异&#xff0c;例如某个听感上的高升调&#xff0c;不同的调查人员可能会分别描写成 […

Nginx的负载均衡

Nginx不仅可以作为一个web服务器或反向代理服务器&#xff0c;还可以按照权重、轮询、ip_hash、URL_hash等多种方式实现对后端服务器的负载均衡。 负载均衡的概念&#xff1a; 负载均衡就是将负载分摊到多个操作单元上执行&#xff0c;从而提高服务的可用性和相应速度&#xf…

数据仓库、数据中台、数据湖都是什么?

相信很多人都在最近的招聘市场上看到过招聘要求里提到了数据仓库、数据中台&#xff0c;甚至还有数据湖&#xff0c;这些层出不穷的概念让人困扰。今天我就来跟大家讲一讲数据仓库、数据中台以及数据湖的概念及区别。 数据库 在了解数据仓库、数据中台以及数据湖之前&#xff…

JDBC

JDBC核心技术 讲师&#xff1a;宋红康 微博&#xff1a;尚硅谷-宋红康 第1章&#xff1a;JDBC概述 1.1 数据的持久化 持久化(persistence)&#xff1a;把数据保存到可掉电式存储设备中以供之后使用。大多数情况下&#xff0c;特别是企业级应用&#xff0c;数据持久化意味着将内…

【java web篇】Tomcat的基本使用

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

ceph介绍、原理、架构、算法...个人学习记录

前言 之前公司安排出差支援非结构化项目&#xff0c;采用springcloud(redismysql数据冷热处理)s3escephkafka还涉及一些区块链技术等等…&#xff0c;在与大佬的沟通交流下对ceph产生了兴趣&#xff0c;私下学习记录一下&#xff1b;后续工作之余会采用上面相关技术栈手动实现不…

Python模块化编程_Python编程之路

之前跟大家讲的是一些python的数据基础&#xff0c;从这篇文章开始&#xff0c;我们开始正式学习python的模块化编程 下面我们解释一下什么叫做模块 之前已经讲过怎么去定义一个方法&#xff0c;如果你是用python交互器(自带交互器&#xff0c;或者是ipython)来学习定义方法的…

4 通道 3.2GSPS(2 通道 6.4GSPS) 12 位 AD 采集子卡模块

FMC134 是一款 4 通道 3.2GSPS&#xff08;或者配置成 2 通道 6.4GSPS&#xff09; 采样率的 12 位 AD 采集 FMC子卡模块&#xff0c;该板卡为 FMC标准&#xff0c;符 合 VITA57.4 规范&#xff0c;可以作为一个理想的 IO 模块耦合至 FPGA 前端&#xff0c; 射频模拟信号数字化…

c语言经典例题-循环结构程序设计

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 求各位数字之积&#xff1a; 本关任务&#xff1a;计算正整数num的各位上的数字之积。 例如&#xff1a; 输入&am…

Elasticsearch:构建自动补全功能 - Autocomplete

什么是自动补全&#xff08;autocomplete&#xff09;功能呢&#xff1f;我们举一个很常见的例子。 每当你去谷歌并开始打字时&#xff0c;就会出现一个下拉列表&#xff0c;其中列出了建议。 这些建议与查询相关并帮助用户完成查询。 Autocomplete 正如维基百科所说的&#xf…

vulnhub raven2复现

1.扫描全网段&#xff0c;找出了存活主机ip为192.168.85.144 nmap 192.168.85.0/24 2.nmap扫描端口 nmap -p1-65535 192.168.85.144 3.访问此网站&#xff0c;没找到什么地方可以利用漏洞 &#xff0c;查看中间件为wordpress 4.使用dirb对该网站进行目录扫描 dirb http://1…

刷题笔记3 | 203. 移除链表元素、707设计链表,206.反转链表

目录 203. 移除链表元素 707、设计链表 206.反转链表 203. 移除链表元素 题意&#xff1a;删除链表中等于给定值 val 的所有节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5] 示例 2&#xff1a; 输入&#xff1a;h…

18跨越语言:不同语言间进行RPC通信

在最开始介绍gRPC时我们讲到,gRPC具有灵活的兼容性,可以支持很多种编程语言,下面我们就使用在后端领域最常用的两种编程语言Go和Java,来体验一下gRPC在不同语言的项目间是如何进行通信的。 逻辑架构 由上图我们可以看出,Go语言设计gRPC的服务端,Java语言设计gRPC的客户端…

欢迎来到 BharatBox,这是一个以来自印度的知名艺术家和品牌为特色的文化元宇宙中心

通过 Brinc 的客户 Heftyverse 娱乐公司&#xff0c;将印度艺术家、电影制片厂、体育品牌和音乐公司聚集在这个全新虚拟中心。 The Sandbox 与 Brinc 的联营公司推出 BharatBox&#xff0c;这是一个全新的文化中心&#xff0c;由来自印度的娱乐、艺术和体育范畴的主要合作伙伴组…

吴恩达监督机器学习:回归和分类(一)

文章目录机器学习简介机器学习是什么quiz监督学习回归问题分类问题quiz无监督学习聚类问题quiz练习测试&#xff1a;监督学习与无监督学习线性回归线性回归模型第一部分基本术语线性回归模型第二部分quiz选学实验&#xff1a;线性回归模型代价函数公式quiz代价函数的直观理解qu…

SpringBoot+Seata在多数据源和feign中的简单使用

SpringBootSeata简单使用 目录seata执行过程安装seata下载seata使用自定义配置文件,NACOS为注册中心结合springboot实现AT模式1.多数据源引入依赖bootstrap.yml配置在使用的方法上用GlobalTransactional注解调用接口正常时调用接口报错时回滚2.配合feignseata优缺点seata执行过…

「并发编程实战」接口幂等性设计的最佳实现(8种实现方案)

「并发编程实战」接口幂等性设计的最佳实现&#xff08;8种实现方案&#xff09; 文章参考&#xff1a; 实战&#xff01;聊聊幂等设计 基于幂等表思想的幂等实践 追忆四年前&#xff1a;一段关于我被外企CTO用登录注册吊打的不堪往事 弹力设计篇之“幂等性设计” 一、什么是幂…