C++学习————第八天(C/C++内存管理)

目录

1、1.C/C++内存分布

[2、 C语言中动态内存管理方式:malloc/calloc/realloc/free](#2、 C语言中动态内存管理方式:malloc/calloc/realloc/free)

3、C++内存管理方式

[3.1 new/delete操作内置类型](#3.1 new/delete操作内置类型)

[3.2 new和delete操作自定义类型](#3.2 new和delete操作自定义类型)

[4.operator new与operator delete函数](#4.operator new与operator delete函数)

[5. new和delete的实现原理](#5. new和delete的实现原理)

[5.1 内置类型](#5.1 内置类型)

[5.2 自定义类型](#5.2 自定义类型)

>)delete注意事项

1、1.C/C++内存分布

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

cpp 复制代码
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. 选择题:
  选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
  globalVar在哪里?__C__  staticGlobalVar在哪里?__C__
  staticVar在哪里?__C__  localVar在哪里?__A__
  num1 在哪里?__A__
  
  分析:
  globalVar全局变量在数据段 staticGlobalVar静态全局变量在静态区
  staticVar静态局部变量在静态区  localVar局部变量在栈区
  num1局部变量在栈区

  char2在哪里?__A__  *char2在哪里?__A__
  pChar3在哪里?__A__   *pChar3在哪里?__D__
  ptr1在哪里?__A__    *ptr1在哪里?__B__
  
  分析:
  char2局部变量在栈区  
  char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上
  pChar3局部变量在栈区   *pChar3得到的是字符串常量字符在代码段
  ptr1局部变量在栈区     *ptr1得到的是动态申请空间的数据

【说明】

1、栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的,栈可以通过函数_alloca进行动态分配,不过注意,所分配空间不能通过free或delete进行释放,频繁的申请空间和释放空间,容易造成内存碎片,甚至内存泄漏,栈区由于是自动管理,不存在此问题

2、内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。(现在只需要了解, 后面在linux中会提到)

3、堆用于程序运行时动态内存分配,堆是可以上增长的。堆无法静态分配,只能动态分配

4、数据段--存储全局数据和静态数据。

5、代码段--可执行的代码/只读常量。


2、 C语言中动态内存管理方式:malloc/calloc/realloc/free

cpp 复制代码
void Test()
{
	int* p1 = (int*)malloc(sizeof(int));
	free(p1);
	// 1.malloc/calloc/realloc的区别是什么?
	int* p2 = (int*)calloc(4, sizeof(int));
	int* p3 = (int*)realloc(p2, sizeof(int) * 10);
	// 这里需要free(p2)吗?
	free(p3);
}
  1. malloc/calloc/realloc的区别?

三者都是分配内存,都是stdlib.h库里的函数,但是也存在一些差异。

(1)malloc函数。其原型void *malloc(unsigned int num_bytes);

num_byte为要申请的空间大小,需要我们手动的去计算,如int *p = (int*)

**malloc( 20*sizeof(int)),**如果编译器默认int为4字节存储的话,那么计算结果是80Byte,一次申请一个80Byte的连续空间,并将空间基地址强制转换为int类型,赋值给指针p,此时申请的内存值是不确定的。

(2)calloc函数,其原型void *calloc(size_t n, size_t size);

其比malloc函数多一个参数,并不需要人为的计算空间的大小,比如如果他要申请20个int类型空间,会int *p = (int *)calloc(20, sizeof(int)),这样就省去了人为空间计算的麻烦。但这并不是他们之间最重要的区别,malloc申请后空间的值是随机的,并没有进行初始化,而calloc却在申请后,对空间逐一进行初始化,并设置值为0;

既然calloc不需要计算空间并且可以直接初始化内存避免错误,那为什么不直接使用calloc函数,那要malloc要什么用呢?

实际上,任何事物都有两面性,有好的一面,必然存在不好的地方。这就是效率。calloc函数由于给每一个空间都要初始化值,那必然效率较malloc要低,并且现实世界,很多情况的空间申请是不需要初始值的,这也就是为什么许多初学者更多的接触malloc函数的原因。

(3)realloc函数和上面两个有本质的区别,其原型void realloc(void *ptr, size_t new_Size)
用于对动态内存进行扩容(及已申请的动态空间不够使用,需要进行空间扩容操作,ptr为指向原来空间基址的指针, new_size为接下来需要扩充容量的大小。


3、C++内存管理方式

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

3.1 new/delete操作内置类型

cpp 复制代码
void Test()
{
	//内置类型

	// 管理对象
	// 动态申请一个int类型的空间
	int* ptr4 = new int;
	// 动态申请一个int类型的空间并初始化为10
	int* ptr5 = new int(10);

	//管理对象数组
	// 动态申请10个int类型的空间
	int* ptr6 = new int[3];
	//动态申请10个int类型的空间,并初始化为1 2 3 4
	int* ptr7 = new int[10] {1, 2, 3, 4}; //后面的会默认初始化为0
	
	//释放一个对象
	delete ptr4; 
	delete ptr5;
	//释放多个对象
	delete[] ptr6; 
	delete[] ptr7;
}

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

3.2 new和delete操作自定义类型

new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数

cpp 复制代码
class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		//cout << "A():" << this << endl;
		cout << "A():" << endl;

	}
	A(int a1, int a2)
	{
		cout << "A(int a1, int a2)" << endl;
	}
	
	A(const A& aa)
		: _a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}

	~A()
	{
		//cout << "~A()" << this << endl;
		cout << "~A()" << endl;

	}
private:
	int _a;
};

int main()
{
	// new/delete 和 malloc/free最大区别是
	// new/delete对于【自定义类型】除了开空间,还会调用构造函数和析构函数
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(2);
	free(p1);
	delete p2;

	// 内置类型是几乎是一样的
	int* p3 = (int*)malloc(sizeof(int)); // C
	int* p4 = new int;
	free(p3);
	delete p4;

	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10]; //调用10次构造函数
	free(p5);
	delete[] p6; //调用10次析造函数

	//多个对象
	A aa1(1);
	A aa2(2);
	A aa3(3);
	A* p7 = new A[10]{ aa1,aa2,aa3 };
	delete[]p7;
	// 多参数
	A* p7 = new A[10]{ 1,2,3,4,5,{6,7}};//用1生成临时对象,然后被编译器合二为一(隐式对象转换)
	delete[]p7;


	return 0;
}

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会

4.operator new与operator delete函数

1.new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数

2.new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局 函数来释放空间。

5. new和delete的实现原理

5.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:

new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

5.2 自定义类型

new的原理

  1. 调用operator new函数申请空间

  2. 在申请的空间上执行构造函数,完成对象的构造
    delete的原理

  3. 在空间上执行析构函数,完成对象中资源的清理工作

  4. 调用operator delete函数释放对象的空间
    new T[N]的原理

  5. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对

象空间的申请

  1. 在申请的空间上执行N次构造函数
    delete[]的原理

  2. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

  3. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释

放空间
总结:new == operator new -> (malloc) + 构造函数

~~~~~~~~~~ delete == 析构函数 + operator delete -> (free)

~~~~~~~~~~ 特别的:new失败了,抛异常, 不需要再检查返回值

>)delete注意事项

cpp 复制代码
ClassA *pclassa=new ClassA[5];
delete pclassa;

析构函数此时只会调用1次,要想完整释放数组空间,需要使用[]
注意:程序可能崩溃

相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习