C/C++ 动态内存管理(内存是如何分布的?malloc/new,free/delete的用法是什么?区别是什么?)

news2024/11/28 2:36:49

目录

一、前言

二、C/C++中的内存分布 

💦了解内存区域的划分

💦内存存储区域的对比和注意点 

💦内存管理的常考面试题

 三、C语言的动态管理方式

四、C++的动态管理方式

 💦new / delete 操作内置类型(int,char.....)

💦new / delete 操作自定义类型 (类,结构体...)

 💦new / delete 操作符的应用场景(单链表节点的创建)

 💦new / malloc,free / delete 之间的区别

五、operator new与operator delete函数

六、new和delete的实现原理

 💦内置类型

💦自定义类型 

 💦应用场景使用(数据结构----栈)

七、C/C++ 常考面试题

 💦malloc/free和new/delete的区别

 💦什么是内存泄漏,内存泄漏的危害

八、常见的笔试题 

九、总结 

十、共勉 


一、前言

   在之前的学习中,我们已经非常了解C语言中的内存管理malloc、calloc、realloc、free等内存管理操作函数,如果有老铁还不太清楚上述的内存管理,可以先去看看这篇文章,有助于大家更好的解C++中的内存管理:动态内存分配:malloc、calloc、realloc、free

   那么在C++中,祖师爷又提出了新的动态内存管理 new、delete,大家有没有想过,已经有了C语言中的动态内存管理为什么还要创造新的呢? 于是带着这样的疑问,我们一起去深入了解以下吧!(主要是我搞不清楚,记录下来,方便后期遗忘😂)

二、C/C++中的内存分布 

💦了解内存区域的划分

        首先我们要先来了解一下内存中的五大区域划分,总共是有【栈区】、【堆区】、【共享段库】、【静态区/数据段】、【代码段】

🍩1 栈:       

         通常是用于那些在编译期间就能确定存储大小的变量的存储区用于在函数作用域内创建,在离开作用域后自动销毁的变量的存储区。通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的。栈的大小是有限的,通常Visual C++编译器的默认栈的大小为1MB,所以不要定义int a[1000000]这样的超大数组。

🍞2 堆:

        通常是用于那些在编译期间不能确定存储大小的变量的存储区它的存储空间是不连续的,一般由malloc(或new)函数来分配内存块,并且需要用free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题需要注意的是,两个紧挨着定义的指针变量,所指向的malloc出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达4GB

🍙3 共享段库(了解):
       
通常包含了文件映射、动态库【包含了可以被程序运行时动态加载的代码和数据】、匿名映射【将内存映射到进程地址空间的方式,而不是映射具体文件】

🎂4 静态区/数据段:
        
 和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。

🍺5代码段(常量存储区):

        和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量的存储区,并且在程序运行期间,存储区内的常量是全局可见的。这是一块比较特殊的存储去,他们里面存放的是常量,不允许被修改。

 

💦内存存储区域的对比和注意点 

 ⭐:内存区域的总体分布图:


 ⭐:根据上面的内容,分别将栈和堆、全局/静态存储区和常量存储区进行对比,结果如下:


 ⭐:注意点
1.栈区:主要用来存放局部变量, 传递参数, 存放函数的返回地址。

2.堆区:用于存放动态分配的对象, 当你使用 malloc和new 等进行分配时,所得到的空间就在堆中。动态分配得到的内存区域附带有分配信息, 所以你能够 free和delete它们。
3. 数据区:全局,静态和常量是分配在数据区中的,数据区包括bss(未初始化数据区)和初始化数据区。

4. 堆向高内存地址生长;

5. 栈向低内存地址生长;

6.堆和栈相向而生,堆和栈之间有个临界点,称为stkbrk。

💦内存管理的常考面试题

 我们先来看下面的一段代码和相关问题:

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
	static int staticVar = 1;
	int localVar = 1;
	int num1[10] = { 1, 2, 3, 4 };
	char char2[] = "abcd";
	const char* pChar3 = "abcd";
	int* ptr1 = (int*)malloc(sizeof(int) * 4);
	int* ptr2 = (int*)calloc(4, sizeof(int));
	int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
	free(ptr1);
	free(ptr3);
}


答案分析:

1.选择题(从左往右):CCCAA  AADAB

  • 很明显前三个globalVarstaticGlobalVarstaticVar都是存放在数据段(静态区)的,其生命周期是从程序开始到结束为止。而 localVar 、 num1都是临时创建的,所以存放在栈区。

  • 然后对于*char2来说,很多同学就会认为它是在【常量区】中的,还记得我们在所谈到字符数组吗,其数组名为首元素地址,那我们对首元素地址去进行解引用的话就拿到了首字符的地址,那么这只是一个字符而已,并不是一个字符串,所以是存放在【栈区】中的
  • 那对于*pChar3呢,很明显它是pChar3是一个指针,其指向的是【常量区】中的一个常量字符串,此时对这个指针去进行解引用也就找到了这个字符串,那么*pChar3即存放在【常量区】中
  • 最后就是*ptr1,它指向的是堆区中的一块空间,*解引用即存放在【堆区】中

2.填空题(从左往右):40、5、4、4/8、4、4/8

  • 首先num1是一个具有10个空间的整型数组,初始化了前4个数据为1、2、3、4,那sizeof(num)即为40
  • char2这个字符数组里面存放着一个字符串,那使用【sizeof()】去进行求解的话会去统计加上\0之后一共有多少个字符,那很明显就是5。【strlen()】的话是请求从字符串首到\0为止的字符个数,不计算\0,那么就一共有4个字符
  • 接下去是sizeof(pChar3),要知道它可是个指针,那对于指针来说均为 4/8 取决于当前的运行环境是32位还是64位的,那么strlen(pChar3)即是在求解这个字符串的长度,即为4
  • 最后则是sizeof(ptr1),它也是一个指针,所以大小为 4/8 个字节

3.sizeof 和 strlen 的区别?


👉 sizeof() 是操作符,不是函数,它是用来计算对象或者类型创建的对象所占内存空间的大小
👉 sizeof() 是操作符,不是函数,它是用来计算对象或者类型创建的对象所占内存空间的大小



看完了上面的这些题后,我们再来在通过画图来进行一个对照,就可以看得非常清晰了


 

 三、C语言的动态管理方式

 malloc / calloc / realloc / free
这部分内容我在C语言的博客中有详细全面的讲解,可以点击这块链接查看:C语言动态内存管理
这边给出代码演示:

void Test()
{
	// 开辟 一个 int 类型的空间
	int* p1 = (int*)malloc(sizeof(int));

	// 开辟 四个 int 类型的空间
	int* p2 = (int*)malloc(4 * sizeof(int));

	// 在p2 的基础上重新分配空间  申请 10个 int 类型的空间
	int* temp = (int*)realloc(p2, sizeof(int) * 5);

	p2 = temp;
	// 将内存释放;
	free(p1);
	free(p2);
}
  • malloc:

在内存的动态存储区中分配一块长度为size字节的连续区域,参数size为需要内存空间的长度,返回该区域的首地址

  • calloc:

与malloc相似,不过函数calloc() 会将所分配的内存空间中的每一位都初始化为零

  • realloc:

 给一个已经分配了地址的指针重新分配空间,可以做到对动态开辟内存大小的调整。

 

四、C++的动态管理方式

        C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过newdelete 操作符 进行动态内存管理。

 💦new / delete 操作内置类型(int,char.....)

  • 接下去就让我们来看在C++中如何使用new这个关键字来动态申请空间
// 动态申请一个int类型的空间
int* p1 = new int;

// 动态申请一个int类型的空间并初始化为10
int* p2 = new int(10);

// 动态申请10个int类型的空间
int* p3 = new int[10];
  • 那既然申请了,我们就要去释放这些空间,C语言中使用free,但是在C++中呢,我们使用delete,对于普通的空间我们直接delete即可,但是对于数组来说,我们要使用delete[]这点要牢记了
delete p1;
delete p2;
delete[] p3;
  • 要知道,在C语言中我们使用malloc在开辟出空间的时候无法去做到初始化,那C++中的new呢,可以吗?通过调试我们可以观察到除了p2所指向的那块空间初始化了,其余都没有,那就可以说明它是可以去一个初始化工作的

  • 可以看到,对于单块的内存区域,只需要使用new 数据类型(初始化数值)的方式即可;而对于像数组这样的空间,我们要使用new int[5]{初始化数值}的形式去进行,此时才可以做到一个初始化
int* p2 = new int(10);
int* p3 = new int[5]{ 1,2,3,4,5 };

  将以上代码进行整合

void Test()
{
	// 开辟 一个 int 类型的空间
	int* p = new int;
	cout << "*p空间存储的值为: " << *p << endl;
	cout << endl;
	// 开辟一个 int 类型的空间,并初始化为10
	int* p1 = new int(10);
	cout << "*p1空间存储的值为: " << *p1 << endl;
	cout << endl;
	// 开辟10个 int 类型的空间,没有初始化
	int* p2 = new int[10];
	cout << "*p2空间存储的值为:" << endl;
	for (int i = 0; i < 10; i++)
	{
		cout << *(p2 + i) << " ";
	}
	cout << endl;
	cout << endl;
	// 开辟10个 int 类型的空间,并初始化前5个值
	int* p3 = new int[10] {1, 2, 3, 4, 5};
	cout << "*p3空间存储的值为:" << endl;
	for (int j = 0; j < 10; j++)
	{
		cout << *(p3 + j) << " ";
	}
	cout << endl;

	delete p;
	delete p1;
	delete[] p2;
	delete[] p3;
}


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


注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[ ]和delete[ ]

⚡总结:对于内置类型而言,用malloc和new,除了用法不同,没有什么区别。它们的区别在于自定义类型
 

💦new / delete 操作自定义类型 (类,结构体...)

 先给出结论:

  • 申请空间时:malloc只开空间,new既开空间又调用构造函数初始化。
  • 释放空间时:delete会调用析构函数,free不会

先看下malloc和free:

class Test
{
public:
	//构造函数
	Test(int x = 1):_day(x)  //初始化列表
	{
		cout << "Test.()" << this << endl;
	}
	// 析构函数
	~Test()
	{
		cout << "~Test.()" << this << endl;
	}
private:
	int _day;
};

void Test1()
{
	// 申请单个Test类型的空间
	Test* p1 = (Test*)malloc(sizeof(Test));
	// 申请10个Test类型的空间
	Test* p2 = (Test*)malloc(sizeof(Test)*10);

	free(p1);
	free(p2);
}
int main(){
	Test1();
	return 0;
}

很明显,malloc的对象只是开辟了空间,并没有初始化,free后也只是普通的释放。

先看下new和delete:

class Test
{
public:
	//构造函数
	Test(int x = 1):_day(x)  //初始化列表
	{
		cout << "Test.()" << this << endl;
	}
	// 析构函数
	~Test()
	{
		cout << "~Test.()" << this << endl;
	}
private:
	int _day;
};
void Test2()
{
	// 申请单个Test类型的空间
	Test* p3 = new Test(5);
	// 申请10个Test类型的空间
	Test* p4 = new Test[5]{1,2,3,5,4};

	delete p3;
	delete[] p4;
}

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

很明显,使用new,既可以开辟空间,又调用了构造函数从而完成初始化,而delete时调用了析构函数,以此释放空间。

注意:
在自定义类型中,malloc出来的一定要用free,而new出来的一定要用delete,千万不可混用了!!!
 

 💦new / delete 操作符的应用场景(单链表节点的创建)

        在C语言中,如果我们需要创建一个单链表的节点,并且进行初始化,是需要花费不少的功夫,还需要调用BuyListNode()函数,很是麻烦如下代码:

// C语言的单链表创建一个节点

typedef struct ListNode
{
	int val;
	struct ListNode* next;
}LN;

LN* BuyListNode(int x)
{
	LN* node = (LN*)malloc(sizeof(LN));
	if (nullptr == node)
	{
		perror("faill malloc");
		exit(-1);
	}
	node->val = x;
	node->next = NULL;
	return node;
}

int main()
{
	LN* n1 = BuyListNode(1);
	LN* n2 = BuyListNode(2);
	LN* n3 = BuyListNode(3);
    return 0;
}

        但如果用C++的话就不一样了,我们可以使用之前所学的过的构造函数初始化列表在开辟出空间的时候就做一个初始化工作,做到事半功倍。代码如下:

// C++ 创建一个单链表的节点

struct ListNode
{
	int _val;
	struct ListNode* next;
	//构造函数 
	ListNode(int val):_val(val),next(nullptr)   //初始化列表
	{}
};

int main()
{
	ListNode* n1 = (ListNode*)malloc(sizeof(ListNode));
	ListNode* n2 = new ListNode(10);
	ListNode* n3 = new ListNode(30);
	return 0;
}


        通过调试我们可以观察到 n2,n3,开辟出了空间并进行了初始化的工作,最重要的是C++的代码量要远少于C语言的代码量,却达到了相同的效果。

        所以经过上面的观察我们可以知道在C++中使用new是会区自动调用构造函数并完成初始化
 

 💦new / malloc,free / delete 之间的区别

1️⃣:在内置类型中,new  和  malloc 的作用是一样的,都是去开辟空间。

                                  free  和  delete  的作用是一样的,都是去释放空间


2️⃣:在自定义类型中,malloc只开空间,new既要开空间又调用构造函数初始化

                                     free 只是释放空间,delete 既要调用析构函数有释放空间

3️⃣:new和delete是C++的关键字/操作符,而malloc和free是C语言的库函数。

4️⃣:malloc的返回值是void*,使用时需要强转,new后边跟的是空间的类型,所以new不需要强转。

5️⃣:new 和 malloc 在申请内存失败时的处理情况不同。

  •                                  malloc如若开辟内存失败,会返回空指针
  •                                  new如若开辟内存失败,会抛出异 

场景验证:当开辟的空间过大时,就会出现内存开辟失败的情况:

int main()
{
	void* p4 = new char[1024 * 1024 * 1024];
	cout << p4 << endl;
    void* p3 = malloc(1024 * 1024 * 1024); //1G
	cout << p3 << endl;
}

        此段测试充分说明了我先开辟1G的大小是没有问题的,但是再开辟1个G的大小就会报错了,为了能够看出malloc和new均报错的场景,我们再定义一个指针占据这1G:

 
         此段测试更能够清楚的看出mallloc失败会返回空指针,而new失败会抛异常。 对于抛异常,我们理应进行捕获,不过这块内容我后续会讲到,这里先给个演示:
 

五、operator new与operator delete函数

        newdelete是用户进行动态内存申请和释放的操作符operator new operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

  • 注意:operator new和operator delete不是对new和delete的重载,这是俩库函数。

        operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。operator new本质是封装了malloc。operator delete本质是封装了free。

  • 具体使用operator new和operator delete的操作如下:
int main()
{
	Stack* ps2 = (Stack*)operator new(sizeof(Stack));
	operator delete(ps2);
 
	Stack* ps1 = (Stack*)malloc(sizeof(Stack));
    assert(ps1);
	free(ps1);
}

        operator new和operator delete的功能和malloc、free一样。也不会去调用构造函数和析构函数,不过还是有区别的,1、operator new不需要检查开辟空间的合法性。2、operator new开辟空间失败就抛异常。

  • operator new和operator delete的意义体现在new和delete的底层原理:
Stack* ps3 = new Stack;
new的底层原理:转换成调用operator new + 构造函数
delete ps3;
delete的底层原理:转换成调用operator delete + 析构函数

        new的底层原理就是转换成调用operator new + 构造函数,我们可以通过查看反汇编来验证:

 

         delete也是转换成调用operator delete + 析构函数,这里画图演示总结:

六、new和delete的实现原理

        在上一小节中,我们学习到了两个全局函数, 分别是【operator new】和【operator delete】,通过分析可以得出它们的底层都是基于【malloc】和【free】来进行实现的。本小结呢,我们继续回归C++中的newdelete,来讲它们的底层实现原理

 💦内置类型

        如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

💦自定义类型 

new的原理

  • 调用operator new函数申请空间
  • 在申请的空间上执行构造函数,完成对象的构造

delete的原理

  • 在空间上执行析构函数,完成对象中资源的清理工作
  • 调用operator delete函数释放对象的空间

new T[N]的原理

  • 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  • 在申请的空间上执行N次构造函数

delete[ ]的原理

  • 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  • 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间


下面是具体的原理实现图,对照着看更好一些

 💦应用场景使用(数据结构----栈)

        有了理论基础后,接下去我们就通过代码来进行一个加深理解。可以看到这里是有一个Stack类,我们要实现的就是在堆上去申请一个栈对象,那又涉及【堆】,又涉及【栈】,该如何去理解呢?

 

看以下代码:

class Stack
{
public:
	Stack(int capacity = 4)
		: _a(new int[capacity])
		, _size(0)
		, _capacity(capacity)
	{
		cout << "Stack(int capacity = 4)" << endl;
	}
	~Stack()
	{
		delete[] _a;
		_size = _capacity = 0;	
		cout << "~Stack()" << endl;	 
	}
private:
	int* _a;
	int _size;
	int _capacity;
};
int main()
{
	//1
	Stack st;

	//2
	Stack* ps = new Stack;
	delete ps;
	
	return 0;
}

📝说明:

七、C/C++ 常考面试题

 💦malloc/free和new/delete的区别

共同点:

  • 都是从堆上申请空间,并且需要用户手动释放。

不同点:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常(底层区别)
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理(底层区别)

​​​

 💦什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:

  • 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。(内存泄漏是指针丢了)

内存泄漏的危害:

  • 长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死
void MemoryLeaks()
{
	// 1.内存申请了忘记释放
	int* p1 = (int*)malloc(sizeof(int));
	int* p2 = new int;
	// 2.异常安全问题
	int* p3 = new int[10];
	Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
	delete[] p3;
}

如何避免内存泄漏:

  • 内存泄漏非常常见,解决方案分为两种:1、事前预防型。如智能指针等。2、事后查错型。如泄漏检测工具。

八、常见的笔试题 

  1. C++中关于堆和栈的说法,哪个是错误的:( C )

    A.堆的大小仅受操作系统的限制,栈的大小一般较小
    B.在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题
    C.堆和栈都可以静态分配
    D.堆和栈都可以动态分配

📝解析:

A. 堆大小受限于操作系统,而栈空间一般由系统直接分配

B. 频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题

C. 堆无法静态分配,只能动态分配(malloc / new)

D. 栈可以通过函数 _alloca 进行动态分配,不过注意,所分配空间不能通过free或delete进行释放


2. 使用 char* p = new char[100]申请一段内存,然后使用delete p释放,有什么问题?( B )

    A.会有内存泄露
   B.不会有内存泄露,但不建议用
   C.编译就会报错,必须使用delete []p
   D.编译没问题,运行会直接崩溃

📝解析:

A. 因为delete内部封装了free,所以对于内置类型而言,可以做到精确释放,不会造成内存泄漏

B. 正确。不会造成内存泄漏,应该用delete[]

C. 编译不会报错,建议针对数组释放使用delete[], 如果是自定义类型,不使用方括号就会运行时错误

D. 对于内置类型,程序不会崩溃,但不建议这样使用
 

九、总结 

  • 在一开始,先是介绍了C/C++的内存分布,分别有【栈区】、【堆区】、【共享区】、【静态区】、【代码段】,它们各自有各自的所需要存放的变量,每一块区域都有这它们不同的特点,理解这一块可以为下文的学习打上一个良好的基础
  • 接下去呢,我们开始谈到C语言的动态内存管理方式,其实就是我们在C语言中所介绍的malloc、calloc、realloc、free这些内存函数,也当时做了一个回顾。看完它们之后我们就开始介绍C++中是如何实现动态内存管理,使用到的关键字为new/delete,其不仅可以去操作内置类型,也可以去操作自定义类型,其会去调用构造函数并初始化,调用析构函数清理空间

  • 在学习完new/delete之后,我们便开始拓展学习了两个全局函数,分别是operator new和operator delete,通过汇编的查看发现了new/delete在底层就会去调用二者,透过观察源码,了解到了原来其内部还调用了[malloc]和[free]这两个内存函数,这似乎增长了我们了我们的知识面
  • 最后,又聊了聊我们在内存这一块的常见面试题,对于这个我也要重点提一句:大家千万不要去死记硬背,一定在理解的基础上去进行记忆,这样才能达到事半而功倍的效果

十、共勉 

         以下就是我对C/C++ 动态内存管理的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++ STL库的理解,请持续关注我哦!!!  

 

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

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

相关文章

5 Paimon数据湖之表数据查询详解

更多Paimon数据湖内容请关注&#xff1a;https://edu.51cto.com/course/35051.html 虽然前面我们已经讲过如何查询Paimon表中的数据了&#xff0c;但是有一些细节的东西还需要详细分析一下。 首先是针对Paimon中系统表的查询&#xff0c;例如snapshots\schemas\options等等这些…

【每日一题】—— C. Anonymous Informant(Codeforces Round 908 (Div. 2))(思维题)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

(动手学习深度学习)第7章 残差网络---ResNet

目录 ResNet总结 ResNet代码实现ResNet的梯度计算 ResNet 总结 残差块使得很深的网络更加容易训练 甚至可以训练一千层的网络 残差网络对随后的深层神经网络设计产生了深远影响&#xff0c;无论是卷积类网络还是全连接类网络。 ResNet代码实现 导入相关库 import torch fro…

K8S容器内安装cur/telnet命令(Alpine Linux离线环境安装curl/telnet或其他工具)

背景 需求&#xff1a; 微服务的基础是镜像&#xff0c;通常在最小化的Linux镜像中安装jdk&#xff0c;然后运行编译好的java程序。将镜像运行到K8S上就得到了微服务Pod&#xff0c;Pod通常使用安装K8S时配置的私有网段&#xff0c;与宿主机不同。很多时候需要排查从Pod网段内…

概念解析 |移动群智感知:智能设备时代的数据革命

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:移动群智感知(Mobile Crowd Sensing, MCS)。 移动群智感知:智能设备时代的数据革命 移动群智感知示意图 背景介绍 随着智能手机和物联网设备的普及,我们进入了一个全新的…

《未来之路:技术探索与梦想的追逐》

创作纪念日 日期&#xff1a;2023年07月05日文章标题&#xff1a;《从零开始-与大语言模型对话学技术-gradio篇&#xff08;1&#xff09;》成为创作者第128天 在这个平凡的一天&#xff0c;我撰写了自己的第一篇技术博客&#xff0c;题为《从零开始-与大语言模型对话学技术-…

【Docker】iptables命令的使用

iptables是一个非常强大的Linux防火墙工具&#xff0c;你可以使用它来控制网络流量的访问和转发。 前面已经学习了iptables的基本原理&#xff0c;四表五链的基本概念&#xff0c;也已经安装好了iptables&#xff0c;下面我们主要学习iptables命令的基本使用。 可以使用iptable…

Redis持久化之RDB和AOF操作

文章目录 前言一、什么是RDB&#xff1f;1.与持久化相关的一些配置2.触发机制3.如何恢复rdb文件4.优点5.缺点 二、什么是AOF&#xff1f;1.重写规则说明2.优点3.缺点 总结 前言 无论是面试还是工作&#xff0c;持久化都是重点! Redis是内存数据库&#xff0c;如果不将内存中的…

阿里云AIGC小说生成【必得京东卡】

任务步骤 此文真实可靠不做虚假宣传&#xff0c;绝对真实&#xff0c;可截图为证。 领取任务 链接&#xff08;复制到wx打开&#xff09;&#xff1a;#小程序://ITKOL/1jw4TX4ZEhykWJd 教程实践 打开函数计算控制台 应用->创建应用->人工智能->通义千问 AI 助手-…

【字符串】【双指针翻转字符串+快慢指针】Leetcode 151 反转字符串中单词【好】

【字符串】【双指针翻转字符串快慢指针】Leetcode 151 反转字符串中单词 解法1 双指针翻转字符串快慢指针更新数组大小 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- ---------------&#x1f388;&#x1f388;解答链接…

矩阵起源荣获第八届“创客中国”深圳市中小企业创新创业大赛三等奖

近日&#xff0c;2023年第八届“创客中国”深圳市中小企业创新创业大赛圆满落下帷幕&#xff0c;矩阵起源&#xff08;深圳&#xff09;信息科技有限公司凭借项目”MatrixOne 新一代超融合异构云原生数据库”荣获企业组三等奖。 本届大赛由深圳市工业和信息化局、深圳市中小企业…

Blazor之Router入门

前言&#xff1a;博主文章仅用于学习、研究和交流目的&#xff0c;不足和错误之处在所难免&#xff0c;希望大家能够批评指出&#xff0c;博主核实后马上更改。 概述&#xff1a; Router 组件允许路由到 Razor 组件。 Router 组件在 App 组件 (App.razor) 中使用。编译带有 p…

鲁大师电动车智能化测评报告第二十三期:实测续航95km,九号Q90兼顾个性与实用

鲁大师第二十三期智能化电动车测评排行榜数据来源于鲁大师智慧实验室,测评的车型均为市面上主流品牌的主流车型。截止目前,鲁大师智能化电动车测评的车型高达130余台,且还在不断增加和丰富中。 一、测评依据 鲁大师电动车智能化测评体系包含车辆的状态采集与管理硬件系统、车辆…

【MySQL】手把手教你centos7下载MySQL

centos7下载MySQL 前言正式开始卸载不需要的环境&#xff08;如果你之前没有安装过数据库相关的东西可以跳过&#xff09;下载mysql登录mysql登陆⽅法⼀【不⾏就下⼀个】登陆⽅法⼆【不⾏就下⼀个】登录方式三 前言 安装和卸载MySQL都用系统的root权限&#xff0c;更方便一点&…

pyqt5学习-01 UI界面创建以及生成python代码

前提 环境搭建 打开designer 选择创建主窗体&#xff0c;拖入一个按钮 保存主窗体UI文件为firstMainWin.ui 将UI文件转化为python文件 # 可以把E:\Python\envs\pyqt5stu\Scripts\pyuic5.exe添加到环境变量中 E:\Python\envs\pyqt5stu\Scripts\pyuic5.exe -o firstMainWin.…

新零售时代,传统便利店如何转型?

在零售批发业&#xff0c;如何降低各环节成本、提高业务运转效率、更科学地了解客户服务客户&#xff0c;是每家企业在激烈竞争中需要思考的课题。 对零售批发企业来说&#xff0c;这些问题或许由来已久&#xff1a; &#xff08;1&#xff09;如何对各岗位的员工进行科学的考…

【nginx】使用arthas协助定位 nginx 499

看到这个499 到服务端 通过arthas查看 并没有耗时很长的 心跳接口 看都是很快的 通过 monitor 命令 通过watch 定位看到这个现象&#xff1a; watch -x 3 在nginx配置文件中添加 在nginx 中查看 没有499 了 再看nginx 中有存在 401 这个是业务问题 剩下的是检测器同事定位…

Flink之SQL查询操作

SQL查询 基本SELECT查询生成测试数据WITHWHEREDISTINCTORDER BYLIMIT 窗口函数概述创建数据表滚动窗口 TUMBLE滑动窗口 HOP累积窗口 CUMULATE窗口偏移 聚合窗口聚合分组聚合OVER聚合 TOP-N普通Top-N窗口Top-N 联结Join查询内部等连接外部等连接间隔联结 集合操作UNION 和 UNION…

内存对齐规则

前言 求结构体的大小是很热门的考点&#xff0c;无论你是学C还是C&#xff0c;都会遇到这样的问题&#xff0c;在面试中也很受欢迎&#xff0c;所以我们先思考这样一个问题&#xff1a;计算结构体&#xff0c;联合体和类的大小应该怎么去计算呢&#xff1f;我们知道&#xff0c…

105.am40刷机(linux)折腾记1-前期的准备工作1

前段时间在某鱼上逛的时候&#xff0c;发现一款3399的盒子只要150大洋&#xff0c;内心就开始澎拜&#xff0c;一激动就下手了3台&#xff0c;花了450大洋&#xff08;现在想想&#xff0c;心都碎了一地&#xff09;。 然后自己又来来回回折腾了几天&#xff0c;目前能跑上fire…