动态内存的管理malloc、free、calloc、realloc

身在井隅,心向星光

眼里有诗,自在远方

目录

动态内存的简单介绍 

动态内存的优势 

可以控制内存的大小

可以多次利用这部分空间

 动态内存函数malloc、free

malloc开辟函数

free释放函数

 动态内存函数calloc、realloc

 calloc开辟函数

realloc调整函数

 动态内存在运用中常出现的错误

1. 对NULL指针的解引用操作:

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

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

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

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

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

 柔性数组

 柔性数组的定义:

柔性数组的特点:

柔性数组的运用 :


我们平时写程序内存不够用是不是手动去扩容

这样程序小浪费空间,程序大了空间不够用

不启用程序时,空间还得不到很好的释放

不用担心我们今天就来解决这类问题

我们今天讲解动态内存的管理


动态内存的简单介绍 

我们先来介绍一下,为什么会有动态内存?

我们学过的两种的内存开辟方法主要是静态内存开辟如下

要么用变量开辟一个空间,要么用数组开辟一个连续的空间

int a = 10;      //在栈空间上开辟四个字节
int a[4] = {0}   //在栈空间上开辟十六个字节
但是上述的静态内存开辟空间的方式有两个缺陷:
空间开辟大小是 固定 的。
数组在申明的时候, 必须指定数组的长度 ,数组空间一旦 确定 了大小 不能调整
内存只有在程序 运行结束只会才销毁不能重复利用

如果我们所需的空间大小只有在程序运行的时候才会知道,那么之前的数组编译开辟内存的方法就得不到满足,于是我们引进了动态内存 

动态内存的优势 

动态内存的优势在于:

可以控制内存的大小

我们可以自己申请和释放内存空间,大了可以调小,小了可以调大,这样程序就比较灵活


可以多次利用这部分空间

动态内存分配的空间,可以通过利用完,就可以 free 这块申请的空间,当再次用动态内存申请空间时,就可以再次利用这块空间,这样也能在一定程度上,可以节省一定的空间

 动态内存函数malloc、free

malloc开辟函数

void* malloc (size_t size);

这个函数向内存申请⼀块连续可用的空间并返回指向这块空间的指针 

malloc所涉及的头文件是#include<stdlib.h> 

  • size -- 内存块的大小,以字节为单位
  • 该函数如果开辟成功,则返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL因此malloc的返回值⼀定要做检查
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己强制类型转换决定
  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器

我们的malloc函数申请的空间是在内存的堆区

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	//开辟一个10个整形字节的空间并用指针p来接收
	if (p == NULL) //判断开辟成功与否
	{
		perror("malloc");//报错信息函数,反应malloc的错误
		return 1;
	}
	//这样我们就可以使用这10个整形字节的空间了
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

 如果开辟的空间巨大,malloc给不了那么大的空间则返回空指针:

现在如果这空间我们不想用了,我们可不可以还回去?

就像借钱一样,不能只借不还,我们可以用free函数来释放空间 

free释放函数

void free (void* ptr);

free函数的作用就是释放动态开辟的内存

free所涉及的头文件是#include<stdlib.h>

  • ptr -- 指针指向一个要释放内存的内存块的起始地址,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的
  • 如果传递的参数是一个空指针,则不会执行任何动作
  • 因为ptr把内存释放后依然指向原来空间的起始地址,会变为野指针,为了避免出现这种情况,所以我们要手动将ptr置为空指针

拿我们刚刚写的代码举例: 

这样写的代码才是完美的

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10* sizeof(int));  //开辟空间
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)            //打印
	{
		printf("%d ", *(p + i));
	}
	free(p);   //释放空间
	p = NULL;  //手动置空
	return 0;
}

malloc、calloc、realloc 申请的空间如果不主动释放,出了作用域是不会销毁的
释放的方式:
1.free主动释放

2.直到程序结束,才由操作系统回收


注意:为了避免发生内存泄漏(一个内存空间没有释放掉不能被其他内容访问,内存泄露堆积后果很严重,无论多少内存,迟早会被占光)我们应该在不使用这块空间的时候及时释放掉


malloc注意: 

要有一点要注意的是malloc函数的初始化并不是0

我把刚刚的赋值循环给撤回了,让我们看看malloc的初始化值是多少

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10* sizeof(int));  //开辟空间
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)            //打印
	{
		printf("%d ", *(p + i));
	}
	free(p);   //释放空间
	p = NULL;  //手动置空
	return 0;
}

我们用十六进制的形式显现出来的就是cd这样一排

因为malloc分配的空间是未初始化的,所以在运行的时候未免出现什么异常,最好初始化一下  


 动态内存函数calloc、realloc

 calloc开辟函数

 

void *calloc(size_t ptr, size_t size)

calloc所涉及的头文件是#include<stdlib.h> 

分配所需的内存空间,并返回一个指向它的指针

malloc 和 calloc 之间的不同点是:

malloc 不会设置内存为零,而 calloc 会设置分配的内存为零


  • ptr   -- 要被分配的元素个数
  • size -- 元素的大小
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(10,sizeof(int));  //开辟空间
	if (p == NULL) 
	{
		perror("calloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)            //打印
	{
		printf("%d ", *(p + i));
	}
	free(p);   //释放空间
	p = NULL;  //手动置空
	return 0;
}

callo与malloc相比的好处就是:将内存初始化为0,在一些情况下可以做到简化代码

realloc调整函数

 realloc所涉及的头文件是#include<stdlib.h> 

void *realloc(void *ptr, size_t size)
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了
那为了合理的时候内存,我们⼀定会对内存的大小做灵活的调整
realloc 函数就可以做到对动态开辟内存大小的调整

作用:尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小

  • ptr  -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针
  • size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针 

realloc在调整失败后直接返回NULL 

realloc在调整内存空间成功 扩容 时是存在两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间  

情况1:在已经开辟好的空间后边,没有足够的空间,直接进行空间的扩大在这种情况下,realloc函数会在内存的堆区重新找一个空间(满足新的空间的大小需求的),同时会把旧的数据拷贝到新的新空间,然后释放旧的空间,同时返回新的空间的起始地址  

 

情况2:在已经开辟好的空间后边,有足够的空间直接进行扩大扩大空间后,直接返回旧的空间的起始地址  

(切记不能直接使用malloc、calloc、 realloc的指针接收,如果realloc一旦开辟失败之前开辟的空间也就找不到了 ) 为了保险起见而是用临时指针ptr来接收并判断是否扩容成功

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10* sizeof(int));  //开辟空间
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 20; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 20; i++)        //打印
	{
		printf("%d ", *(p + i));
	}
	//空间不够,想要扩大空间,80个字节
	int* ptr = (int*)realloc(p, 20 * sizeof(int));
	if (ptr != NULL)    //如果扩容成功,就将ptr赋值给p
	{
		p = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	free(p);   //释放空间
	p = NULL;  //手动置空
	return 0;
}

当然realloc除了扩容还可以缩容

 realloc可以随意调整动态内存空间的大小

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));  //开辟空间
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//空间太大,想要缩小空间,20个字节
	int* ptr = (int*)realloc(p, 5* sizeof(int));
	if (ptr != NULL)    //如果缩容成功,就将ptr赋值给p
	{
		p = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	for (int i = 0; i < 5; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 5; i++)        //打印
	{
		printf("%d ", *(p + i));
	}
	free(p);   //释放空间
	p = NULL;  //手动置空
	return 0;
}

realloc除了调整函数空间之外还可以实现跟malloc一样的功能

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)realloc(NULL, 10 * sizeof(int));
	if (p == NULL) 
	{
		perror("realloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)       
	{
		printf("%d ", *(p + i));
	}
	free(p);   
	p = NULL;  
	return 0;
}

将realloc的指向的空间置为NULL,realloc就可以直接开辟动态内存空间


 

 动态内存在运用中常出现的错误

1. 对NULL指针的解引用操作:

错误代码描述:

int* p = (int*)malloc(max*sizeof(int));
*p = 10;
free(p);

因为如果开辟的内存太大malloc会返回NULL,这个时候对一个NULL指针进行解引用操作编译器就会报错


修正后:

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	else
	{
		*p = 20;
	}
	free(p);
    p = NULL;   

我们只要判断p是否为空,就能避免发生这类情况

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

错误代码描述:

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i <= 10; i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;

我开辟10个整形类型的空间,却要访问11个元素,这样照成了越界访问


修正后:

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;

只要我们以后写代码小心注意一点,就可以避免这个情况

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

错误代码描述:

 int a = 10;
 int* p = (int*)malloc(10 * sizeof(int));
 if (p == NULL) 
 {
	perror("malloc");
	return 1;
 }
 int *p = &a;  //p指向的空间不再是堆区上的空间
 free(p);
 p = NULL;

静态栈区的a地址赋值给p,p所指向的空间就不再是堆区,而free只能释放堆区的空间所以导致程序崩溃


注意:

不能对栈区的空间进行free释放,否则会报错

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

错误代码描述:

 int *p = (int *)malloc(10*(sizeof(int));
 if (p == NULL) 
 {
	perror("malloc");
	return 1;
 }
 p++;
 free(p);
 p = NULL;
p++导致p不再指向动态内存的 起始位置 ,free函数在释放的时候必须是从 起始位置 开始释放,所以导致程序崩溃

注意:

不要随意改变指向动态内存空间指针的位置

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

错误代码描述:

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	free(p);
	free(p);
	p = NULL;

多次释放同一块空间导致程序崩溃


修正后:

	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL) 
	{
		perror("malloc");
		return 1;
	}
	free(p);
    p = NULL;
    free(p);
	p = NULL;

把p重新定义成空指针就可以再次释放空间,不过没任何实际意义。注意不要多次释放同一块空间

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

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

malloc将10个整形的字节的空间赋值给p,但是没有释放空间。p是个局部变量出了函数定义域就销毁了,那么这10个整形的字节就找不到,得不到释放(除非程序结束)。那么这10个整形的字节的空间是不是等于内存泄漏。内存泄漏会导致程序内存占用越来越多,最终导致崩坏

忘记释放不再使用的动态开辟的空间会造成内存泄漏

修改后:
void test()
 {
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL != p)
 {
 *p = 20;
 }
 free(p);
 p = NULL;
 }
int main()
 {
 test();
 while(1);
 }

所以大家在开辟内存后记得释放内存哦


 柔性数组

 柔性数组的定义:

 结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员

struct Stu
{
	int num;
	int arr[];//柔性数组的成员
};
//或者
struct Stu
{
	int num;
	int arr[0];//柔性数组的成员
};

柔性数组的特点:

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

 我们来看一下面这段代码的结构体大小是多少呢?

#include<stdio.h>
struct Stu
{
	int num;
	int arr[];
}S;
int main()
{
	printf("%d\n", sizeof(S));
	return 0;
}

我们发现结构体的大小是4,是一个整形成员的大小,所以柔性数组是不占用结构体空间的


柔性数组的运用 :

 我们来看以下代码:

我们在结构体4个字节的情况下,给结构体多开辟了10个整形的字节,那么这10个整形的字节给谁用了呢?

答案当然是我们的-柔性数组

#include<stdio.h>
#include<stdlib.h>
typedef struct
{
	int num;
	int arr[];
}Stu;
int main()
{
	int i = 0;
	Stu* p = (Stu*)malloc(sizeof(Stu) + 10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	p->num = 100;
	for (i = 0; i < 10; i++)
	{
		p->arr[i] = i;
	}
	printf("%d\n", p->num);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p->arr[i]);
	}
	free(p);
	p = NULL;
	return 0;
}

 像这种用柔性数组扩容结构体,用直接我们的内存函数也可以做到:

我们这样对arr指向的空间进行扩容,使用完后将arr指向的空间释放掉

#include<stdio.h>
#include<stdlib.h>
typedef struct
{
	int num;
	int* arr;
}Stu;
	int main()
	{
		Stu* p = (Stu*)malloc(sizeof(Stu));
		p->num = 100;
		if (p == NULL)
		{
			perror("malloc");
			return 1;
		}
		p->arr = (int*)malloc(10 * sizeof(int));
		if (p == NULL)
		{
			perror("malloc-2");
			return 1;
		}
		printf("%d\n", p->num);
		for (int i = 0; i < 15; i++)
		{
			p->arr[i] = i;
		}
		for (int i = 0; i < 15; i++)
		{
			printf("%d ", p->arr[i]);
		}
		int* ptr = (int*)realloc(p->arr, 15 * sizeof(int));
                //如果空间不够用,进行内存扩容
		if (ptr == NULL)
		{
			perror("realloc");
		}
		else
		{
			p = ptr;
		}
		for (int i = 10; i < 15; i++)
		{
			p->arr[i] = i;
		}
		for (int i = 10; i < 15; i++)
		{
			printf("%d ", p->arr[i]);
		}
		free(p->arr); //释放的是指向arr的空间
		p->arr = NULL;
		free(p);      //释放的是p的空间
		p = NULL;
	return 0;
}

上述 代码1 代码2 可以完成同样的功能,但是柔性数组的实现有两个好处: 

方便内存释放:
柔性数组扩容只需要一次释放,而使用第二种要进行多次释放
有利于访问速度的提高:
连续的内存有益于提高访问速度,也有益于减少 内存碎片 (会让程序内存利用率不高)


本期的动态内存就讲到这里

希望这些知识能让你对动态内存有更深的理解

我们下期再会~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

生产问题: 利用线程Thread预加载数据缓存,其它类全局变量获取缓存偶发加载不到

生产问题: 利用线程Thread预加载数据缓存偶发加载不到 先上代码 public class ThreadTest {//本地缓存Map<String, Object> map new HashMap<String, Object>();class ThreadA implements Runnable{Overridepublic void run() {System.out.println("Thread…

笔记本电脑word打字延迟特别大,但是浏览器中打字没有延迟,如何解决这个问题。

问题描述&#xff1a; 笔记本电脑word打字延迟特别大&#xff0c;但是浏览器中打字没有延迟&#xff0c;如何解决这个问题。&#xff08;之前以为是自己的电脑用了6年&#xff0c;用的时间久了&#xff0c;硬件老化导致的&#xff0c;本来想直接换电脑的&#xff0c;但是想着去…

鸿蒙前端开发-构建第一个ArkTS应用(Stage模型)

创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。 选择Application应用开发&#xff08;本文以应用开发为例&#xff0c;Atomic Serv…

数据库连接池Druid

在 Spring Boot 项目中&#xff0c;数据库连接池已经成为标配&#xff0c;然而&#xff0c;我曾经遇到过不少连接池异常导致业务错误的事故。很多经验丰富的工程师也可能不小心在这方面出现问题。 在这篇文章中&#xff0c;我们将探讨数据库连接池&#xff0c;深入解析其实现机…

探索开源游戏的乐趣与无限可能 | 开源专题 No.47

CleverRaven/Cataclysm-DDA Stars: 9.0k License: NOASSERTION Cataclysm&#xff1a;Dark Days Ahead 是一个回合制的生存游戏&#xff0c;设定在一个后启示录世界中。尽管有些人将其描述为 “僵尸游戏”&#xff0c;但 Cataclysm 远不止于此。在这个残酷、持久、程序生成的世…

抓取真实浏览器设备指纹fingerprint写入cookie方案

一个关于抓取真实浏览器设备指纹写入cookie方案&#xff0c;用户访问页面获取到用户设备生成指纹id&#xff0c;通过js把指纹存入cookie&#xff0c;然后用php进行获取cookie存的指纹值到后台。 用途&#xff1a;追踪用户设备&#xff0c;防恶意注册&#xff0c;防恶意采集 浏…

Android12之解决:scripts/gcc-wrapper.py, line 79, in run_gcc(一百六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

区块链媒体宣发:揭示优势与趋势,引领信息传播新时代

在数字化潮流中&#xff0c;区块链技术正以惊人的速度改变着传媒行业的格局。从区块链媒体宣发中获得的种种优势和未来的趋势&#xff0c;不仅为企业带来了新的推广途径&#xff0c;也在信息传播领域掀起了一场革命。本文将深入探讨区块链媒体宣发的优势以及未来的发展趋势。 1…

深入理解希尔排序

基本思想 希尔排序(Shells Sort)是插入排序的一种又称“缩小增量排序”&#xff08;Diminishing Increment Sort&#xff09;&#xff0c;是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。 对于n个待排序的数列&#xff0c;取一个小于n的整数gap(gap被…

Low Cost and High Performance FPGA with ARM and SDRAM inside

AG10KSDE176 AGM AG10KSDE176 是由 AGM FPGA AG10K 与 SDRAM 叠封集成的芯片&#xff0c;具有 AG10K FPGA 的可编程功能&#xff0c;提供更多可编程 IO&#xff0c;同时内部连接大容量 SDRAM。  FPGA 外部管脚输出 EQFP176 封装底部 Pad 为 GND&#xff0c;管脚说明请见下表&…

计网Lesson8 - NAT技术与链路层概述

文章目录 NAT 技术1. 因特网的接入方式2. 公网和私网3. NAT 技术 链路层1. 数据链路层概述2. 数据链路层的三个问题2.1 封装成帧2.2 透明传输2.3 差错检测 NAT 技术 1. 因特网的接入方式 光猫将电信号转换为数字信号发送给路由器 光纤入户 光纤传递的就是数字信号&#xff0c…

uniapp iOS离线打包——运行项目到模拟器报错?

运行项目、打包时报错问题 记录个人在开发过程中遇到的相关问题&#xff0c;后续有时间会不定时更新 文章目录 运行项目、打包时报错问题运行到模拟器报错解决方案 打包报错解决方案 运行到模拟器报错 解决方案 选中项目工程 —> Build Settings 滑动底部 —> User-Defi…

Java网络编程——安全网络通信

在网络上&#xff0c;信息在由源主机到目标主机的传输过程中会经过其他计算机。在一般情况下&#xff0c;中间的计算机不会监听路过的信息。但在使用网上银行或者进行信用卡交易时&#xff0c;网络上的信息有可能被非法分子监听&#xff0c;从而导致个人隐私的泄露。由于Intern…

Leetcode—389.找不同【简单】

2023每日刷题&#xff08;五十五&#xff09; Leetcode—389.找不同 实现代码 char findTheDifference(char* s, char* t) {int len strlen(s);int len2 len 1;int a[26] {0};int b[26] {0};if(len 0) {return t[0];}for(int i 0; i < len; i) {int idx s[i] - a;…

neuq-acm预备队训练week 8 P1144 最短路计数

题目描述 给出一个 N 个顶点 M条边的无向无权图&#xff0c;顶点编号为 1∼N。问从顶点 1 开始&#xff0c;到其他每个点的最短路有几条。 题目限制 输入格式 第一行包含 22 个正整数 N,M&#xff0c;为图的顶点数与边数。 接下来 M 行&#xff0c;每行 2个正整数 x,y&…

101基于matlab的极限学习机ELM算法进行遥感图像分类

基于matlab的极限学习机ELM算法进行遥感图像分类&#xff0c;对所获取的遥感图片进行初步分类和最终分类。数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。

键盘打字盲打练习系列之成为大师——5

一.欢迎来到我的酒馆 盲打&#xff0c;成为大师&#xff01; 目录 一.欢迎来到我的酒馆二.关于盲打你需要知道三.值得收藏的练习打字网站 二.关于盲打你需要知道 盲打系列教程&#xff0c;终于写到终章了。。。一开始在看网上视频&#xff0c;看到up主熟练的打字技巧&#xff…

【机器学习】041_模型开发迭代过程

一、模型开发的一般步骤 1. 明确研究问题 确定问题的组成和结果&#xff0c;明晰问题是分类问题还是回归问题 2. 决定系统总体架构 ①理解数据&#xff1a;采集&#xff08;爬取&#xff09;数据&#xff0c;生成&#xff08;导入&#xff09;数据&#xff0c;进行数据清洗…

Unity 实现单例模式

目录 基本概念 饿汉模式(推荐) 懒汉模式&#xff1a; 基本概念 单例模式&#xff1a;类只有一个实例&#xff0c;一般使用static来实现单例模式&#xff1b; 比如&#xff1a;有一个Test类,实现了单例&#xff0c;假设这个唯一的实例名为SingTonle,实例在类内被实现并被stat…

【KCC@南京】KCC南京“数字经济-开源行”活动回顾录

11月26日&#xff0c;由KCC南京、中科南京软件研究所、傲空间、PowerData联合主办的 KCC南京“数字经济-开源行” 的活动已圆满结束。此次活动&#xff0c;3 场主题研讨&#xff0c;11 场分享&#xff0c;现场参会人数 60&#xff0c;线上直播观看 3000&#xff0c;各地小伙伴从…