C/C++内存管理

🍬 mooridy-CSDN博客

🧁C++专栏(更新中!)

目录

1. C/C++内存分布

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

3. C++内存管理方式

3.1 new/delete操作内置类型

3.2 new和delete操作自定义类型

4. operator new与operator delete函数

4.1 operator new与operator delete函数(重点!)

5. new和delete的实现原理

5.1 内置类型

5.2 自定义类型

new的原理

delete的原理

new T[N]的原理

delete[]的原理

6. malloc/free和new/delete的区别

7*.匹配使用的原因


1. C/C++内存分布

判断下面代码中的各个变量的内存分布。

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";//char2——栈区,*char2——栈区(首元素)
const char* pChar3 = "abcd";//char3——栈区(局部变量),*char3——常量区
int* ptr1 = (int*)malloc(sizeof(int) * 4);//ptr1——栈区,*ptr1——堆区
free(ptr1);
}

结论:

1.static修饰

      全局变量——静态区

      局部变量——静态区

2.const修饰

     全局变量——静态区

     局部变量——栈

3。动态开辟的空间——堆区

4.字符串数组——静态区/栈

   字符串指针——常量区(只读)

【说明】
1. 又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是 向下增长 的。
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
创建共享共享内存,做进程间通信。
3. 用于程序运行时动态内存分配,堆是可以 向上增长 的。
4. 数据段(静态区)--存储全局数据和静态数据。
5. 代码段(常量区)--可执行的代码/只读常量。

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

  • 1.malloc/calloc/realloc的区别是什么?

  • malloc:void *malloc(unsigned int num_bytes);

  • 用于分配指定字节大小的内存空间,并返回一个指向这块内存的指针。分配的内存空间不会被初始化,其内容是未定义的。

  • num_bytes——要申请的空间大小

  • calloc:void *calloc(size_t n, size_t size);

  • 与 malloc 类似,但它会分配一块连续的内存空间,并且将分配的内存初始化为零。

  • n——需要分配的元素数量,

  • size——每个元素的大小

  • 注:效率较malloc要低

  • realloc:void realloc(void *ptr, size_t new_Size);

  • 用于调整之前通过 malloccalloc 或 realloc 分配的内存块的大小

  • ptr——指向原来空间的指针

  • new_Size——新的空间大小

2.这里需要free(p2)吗?

void Test ()
{
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?
free(p3 );
}

不需要 调用 free(p2),因为它现在指向的内存可能已经被 realloc 释放或重新分配。

3. C++内存管理方式

C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

3.1 new/delete操作内置类型

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[] 注意:匹配起来使用。
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}

3.2 newdelete操作自定义类型

int main()
{
	// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
	//还会调用构造函数和析构函数
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	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];
	free(p5);
	delete[] p6;
	return 0;
}
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与
free不会。

4. operator newoperator delete函数

4.1 operator newoperator delete函数(重点!)

1.new和delete是用户进行动态内存申请和释放的 操作符 operator new 和operator delete
系统提供的全局 函数 new在底层调用operator new全局函数来申请空间,delete在底层通过
operator delete全局函数来释放空间
2.operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的

5. newdelete的实现原理

5.1 内置类型

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

5.2 自定义类型

new的原理

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

delete的原理

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

new T[N]的原理

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

delete[]的原理

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

6. malloc/freenew/delete的区别

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在释放空间前会调用析构函数完成空间中资源的清理释放

7*.匹配使用的原因

class A
{
public:
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a1 = 2;
	int _a2 = 2;
};
class B
{

private:
	int _b1 = 2;
	int _b2 = 2;
};

int main()
{
    
    //1内置类型,new[N]和delete匹配——没有问题
    int* p1 = new int[10]; // -> malloc
	delete p1;             // -> free
	
    //未报错
	B* p2 = new B[10];
	delete p2;

//报错
	//A* p3 = new A[10];
	//delete p3;

	return 0;
}
p2                                                              p3
        
如上图,p3多开了4字节储存个数(提供给析构函数用,调用几次)。而new返回指针并没有指向开辟空间的起点。后续使用的delete针对于单个元素的释放,直接就从p3指针指向处释放,造成内存泄露,编译器不通过。     
为什么这里p2通过?
因为p2没有多开4个字节储存个数。原因:B没有自定义析构函数,编译器对此进行了优化。(编译器检查了B后发现没有必要调用析构函数,因此选择不开空间储存个数。

相关推荐

  1. 内存管理

    2024-07-19 13:48:03       34 阅读
  2. C/C++——内存管理

    2024-07-19 13:48:03       60 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 13:48:03       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 13:48:03       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 13:48:03       58 阅读
  4. Python语言-面向对象

    2024-07-19 13:48:03       69 阅读

热门阅读

  1. 初识synchronized

    2024-07-19 13:48:03       23 阅读
  2. 【QT】001第一个程序

    2024-07-19 13:48:03       19 阅读
  3. 【深度学习】CycleGAN

    2024-07-19 13:48:03       22 阅读
  4. 一篇就够mysql高阶知识总结

    2024-07-19 13:48:03       19 阅读
  5. oracle创建服务

    2024-07-19 13:48:03       22 阅读
  6. 音视频中文件的复用和解复用

    2024-07-19 13:48:03       23 阅读
  7. PHP 调用 JD 详情 API 接口:提升电商体验的关键

    2024-07-19 13:48:03       20 阅读
  8. 域名解析出错的解决办法

    2024-07-19 13:48:03       20 阅读
  9. 裸金属服务器

    2024-07-19 13:48:03       22 阅读