C语言的动态内存管理你了解吗

目录
  • C/C++内存分配方式
  • C++内存管理方式
    • new和delete的使用
    • new和delete的骚操作
    • new和delete的区别
    • 重载new和delete
    • 定位new表达式
    • 内存泄露
  • 总结

C/C++内存分配方式

在学习C语言阶段的时候,创建一个变量,编译器会为它分配一块内存。而创建一个C++对象的时候,编译器会为这个对象分配内存,并且调用合适的构造函数进行初始化。

那么编译器的内存分配方式是怎样的呢?

内存分配可以有以下的几种方式

  • 从静态存储区分配。这样的分配方式在程序开始前就可以为对象/变量分配,这块空间在整个程序运行期间都存在。
  • 从栈区分配。调用函数时,函数的参数,局部变量,返回地址等存储在堆栈上,函数执行结束时将会自动释放这些内存空间,栈区的内存空间远远小于堆区。
  • 从堆区分配。这种内存分配方式被称为动态内存分配,堆区又被称为“自由存储单元”,运行时通过调用相应的函数来申请和释放内存。

有时候我们并不知道程序中的对象确切地需要多少内存空间,动态内存分配则很好地处理了这种需求。

C++内存管理方式

C库中提供了函数malloc,以及它的变种函数realloc、calloc来动态地申请内存空间。使用函数free来释放动态申请出的内存空间。

int* ptr1 = (int*)malloc(sizeof(int));

使用malloc需要指定空间大小,并且要强制类型转化,因为它只是简单地分配了一块空间,返回的是void*,而C++中不允许将空类型的指针赋予给其他类型的指针。另外,如果你申请一块内存之后,没有对这个指针进行正确的初始化,有可能会导致程序运行失败,并且如果忘记释放动态申请的内存空间,则会造成内存泄露等危害……

在创建一个C++对象时,编译器会做这两件事:

1.为对象分配内存。

2.编译器自动调用构造函数初始化该内存。

构造函数不支持显式地调用,意味着如果使用malloc函数创建一个对象,那么这个对象将不能够调用构造函数,仅仅只是开辟了一块空间。但是我们必须要确保对象被初始化,因为未初始化对象是大部分程序出错的主要原因。总而言之,C中的动态内存管理无法满足C++中动态对象的需求。

所以提出了newdelete.关键字

new和delete的使用

int main(void)
{
	//基本内置类型
	//开辟一个int类型的空间
	int* ptr2 = new int;

	//开辟多个int类型空间
	int* ptr3 = new int[5];

	//开辟一个int类型并初始化为1
	int* ptr4 = new int(1);

	delete ptr2;
	delete[] ptr3;
	delete ptr4;
	return 0;
}

new和delete的使用方式:

1.开辟一个空间: new 类型;       对应释放: delete 对象;2.开辟多个空间:new 类型[个数]   对应释放:delete[] 对象;3.开辟并初始化: new 类型(初始化数据) 对应释放:delete 对象  1.开辟一个空间: new 类型;       对应释放: delete 对象;
2.开辟多个空间:new 类型[个数]   对应释放:delete[] 对象;
3.开辟并初始化: new 类型(初始化数据) 对应释放:delete 对象

new和delete的骚操作

内置类型

对于内置类型,new和delete与C的内存管理函数做了差不多的事情,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。

自定义类型

new表达式:

  • 调用opreator new函数分配内存
  • 调用构造函数初始化该内存

delete表达式:

  • 调用析构函数清理对象中的资源
  • 调用operator delete释放空间。

operator new( ) 和operator delete( )这两个内存分配函数是系统提供的全局函数,实际上是对malloc和free的各种行为进行了封装。

new和delete的区别

new、delete 和 malloc、free的区别有哪些呢?

  • new和delete是关键字,malloc和free是函数
  • malloc申请的空间不会初始化,new会初始化。
  • malloc需要手动计算空间大小并传递,new不需要
  • malloc的返回值是void*,使用时必须强制类型转换;new不需要,后面跟的是空间的类型。
  • malloc申请失败,返回NULL,所以调用后要判断是否开辟成功;new需要捕获异常
  • malloc只是开辟空间,不会调用构造函数,free释放空间不会调用析构函数;new在申请空间后会调用构造函数初始化对允许象,delete会调用析构函数清理对象中的资源,然后再释放空间。

重载new和delete

C++允许重载new和delete,以实现我们自己的存储分配方案。但是注意重载operator new和operator delete时,仅仅只能改变原本的内存分配方式。同重载其他的运算符一样,可以分为重载成全局和针对特定类的内存分配函数。

重载全局

重载一个全局的new和delete会导致默认版本完全不能被访问。

重载operator new的要求:

  • 必须有一个size_t参数,该参数将接收要开辟空间的长度。
  • 返回一个指向对象的指针,该对象的长度等于或者大于所申请的长度。
  • 如果分配失败,不仅仅要返回一个0,还需产生一个异常信息之类的现象,明确分配内存时出了问题。
  • 返回值是一个void*

重载operator delete的要求

  • 参数是一个指向由operator new()分配的void*类型的内存的指针
  • 返回值是void

为什么重载operator delete的时候,参数是一个void*?

这是因为它是在调用析构函数后得到的指针。

	//重载operator new
	//1.必须有一个size_t的参数,该参数将接收要申请开辟空间的大小
	//2.返回值是一个void*
	//3.返回一个指向对象的指针,该对象的长度等于或大于所申请的长度
	//4.如果分配失败。不仅仅要返回一个0,还需产生一个异常信息
	void* operator new(size_t sz)
	{
		cout << "new %d Bytes" << sz << endl;
		void* p = nullptr;
		//不需要强制类型转换,因为malloc返回的就是void*
		p = malloc(sz);
		if (nullptr == p)
		{
			cout << "new fail\n" << endl;
		}
		return p;
	}
	//重载operator delete
	//1.返回值为void
	//2.参数是一个指向由operator new()返回的void*的指针
	void operator delete(void* rp)
	{
		cout << "operator delete" << endl;
		free(rp);
	}

重载类专属

//重载ListNode专属的operator new
struct ListNode
{
	ListNode* _next;
	ListNode* _prev;
	int _data;
	void* operator new(size_t n)
	{
		void* p = nullptr;
		p = allocator<ListNode>().allocate(1);
		cout << "memory pool allocate" << endl;
		return p;
	}
	void operator delete(void* p)
	{
		allocator<ListNode>().deallocate((ListNode*)p, 1); //内存池--空间适配器
		cout << "memory pool deallocate" << endl;
	}
};
class List
{
public:
	List()
	{
		_head = new ListNode;
		_head->_next = _head;
		_head->_prev = _head;
	}
	~List()
	{
		ListNode* cur = _head->_next;
		while (cur != _head)
		{
			ListNode* next = cur->_next;
			delete cur;
			cur = next;
		}
		delete _head;
		_head = nullptr;
	}
private:
	ListNode* _head;
};
int main()
{
	List l;
	return 0;
}

定位new表达式

定位new表达式:它的作用是在已分配的原始内存空间中用构造函数初始化一个对象

使用的格式:

new (place_address) type 或者 new (place_address) type (initializer-lost) place_address必须是一个指针,initializer-list是初始化列表。

//例如
class Test
{
public:
	Test()
		: _data(0)
	{
		cout << "Test():" << this << endl;
	}
	~Test()
	{
		cout << "~Test():" << this << endl;
	}

private:
	int _data;
};

int main(void)
{
	// pt现在指向的只不过是与Test对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行
	Test* pt = (Test*)malloc(sizeof(Test));

	//定位new
	new(pt) Test; // 注意:如果Test类的构造函数有参数时,此处需要传参
	return 0;
}

内存泄露

内存泄露的含义:

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

内存泄露的两大分类:

1.堆内存泄露(Heap leak)

堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。

2.系统资源泄露

指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。

内存泄露的危害:

在平时写一些小测试的时候,并没有觉得内存泄露的危害特别大,但是在长期运行的程序中出现内存泄漏,影响非常的大,出现内存泄露可能会导致响应越来越慢,最终出现卡死的现象。

内存泄露的解决方案分两种:

1.事先预防 。

2. 事后查错

如何事先预防?

1.养成良好的编码习惯,申请了内存要记得释放。

2.采用RAII思想或者智能指针来管理资源。

3.规范使用内部实现的私有内存管理库。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C语言动态内存管理介绍

    目录 前言: C 语言为内存的分配和管理提供了几个函数: 1.malloc() 用法 2.calloc() 用法 3.realloc() 与 free() 用法 前言: 简单记录一下,内存管理函数 为什么使用动态内存呢? 简单理解就是可以最大限度调用内存 用多少生成多少,不用时就释放而静止内存不能释放 动态可避免运行大程序导致内存溢出 C 语言为内存的分配和管理提供了几个函数: 头文件:<stdlib.h> 注意:void * 类型表示未确定类型的指针  1.malloc() 用法  分配一块

  • 关于C语言动态内存管理介绍

    目录 1.为什么需要动态内存分配 2.有关动态内存函数介绍 2.1 malloc和free 2.2 calloc函数 2.3 realloc函数 3. 常见的动态内存错误 3.1 对NULL指针进行解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部分 3.5 对同一块动态内存多次释放 3.6 动态开辟内存忘记释放(内存泄漏) 总结 1.为什么需要动态内存分配 关于这个问题,我们先看看我们之前是如何开辟内存的. i

  • C语言 动态内存管理全面解析

    目录 1. 为什么存在动态内存分配 2. 动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 3. 常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部分 3.5 对同一块动态内存多次释放 1. 为什么存在动态内存分配 *动态内存开辟在堆区* 我们已经掌握的开辟内存方式是类型直接定义变量,开辟的内存是固定的,像: int a=

  • 超详细分析C语言动态内存管理问题

    目录 一.为什么存在动态内存的分配 二.动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 三.常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 对同一块动态内存多次释放 3.5 动态开辟内存忘记释放(内存泄漏) 四.几个经典的笔试题 五.C/C++程序的内存开辟 六.柔性数组 6.1 柔性数组的特点 6.2 柔性数组的使用 6.3 柔性数组的优势 上期结束了[

  • 详解C语言之动态内存管理

    目录 开辟动态内存的函数 释放开辟的动态内存空间的函数 错误信息函数 具体使用例: 常见的动态内存错误 总结 先来了解一下动态管理内存所需用到的函数 开辟动态内存的函数 1.malloc函数:void* malloc(size_t size); 功能:开辟一块大小为size单位为字节的动态空间.若开辟成功返回函数开辟空间的无类型指针,若开辟失败则返回空指针NULL 2.calloc函数: void* calloc(size_t num, size_t size); 功能:开辟一块能容纳下num个

  • C语言的动态内存管理的深入了解

    目录 一.动态内存分配 二.动态内存分配函数 1.malloc() 2.realloc() 3.calloc() 三.用free函数释放内存 四.迷途指针 总结 一.动态内存分配 (1)用malloc类的函数分配内存: (2)用这些内存支持应用程序: (3)用free函数释放内存. 内存的简答来说的三大操作:分配----使用----释放 内存管理指的是:分配— ----释放 我们编写的程序代码:使用 程序本质上就是处理数据,数据信息需要存放在内存里,就是用二极管表示的开断表示二进制数,进一步用二

  • C语言动态内存管理分析总结

    目录 什么是动态内存分配 动态内存函数的介绍 free malloc calloc realloc 动态内存管理中常见的错误 对NULL指针的解引用操作 对动态开辟空间的越界访问 对非动态开辟内存使用free释放 使用free释放一块动态开辟内存的一部分 对同一块动态内存多次释放 动态开辟内存忘记释放(内存泄漏) 一些经典的笔试题 题目1 题目2 题目3 题目4 柔性数组 柔性数组的特点 柔性数组的优势 什么是动态内存分配 我们都知道在C语言中,定义变量的时候,系统就会为这个变量分配内存空间,而

  • C语言 超详细梳理总结动态内存管理

    目录 一.为什么存在动态内存分配 二.动态内存函数的介绍 1.malloc和free 2.calloc 3.realloc 三.常见的动态内存错误 1.对NULL指针的解引用操作 2.对动态开辟空间的越界访问 3.对非动态开辟的空间使用free释放 4.使用free释放一块动态开辟空间的一部分 5.对同一块开辟的空间多次释放 6.动态内存开辟忘记释放(内存泄漏) 四.几个经典的笔试题 1. 一.为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int a = 10://在栈空间开辟4个字节

  • C语言初识动态内存管理malloc calloc realloc free函数

    目录 一.为什么存在动态内存分配 二.动态内存函数的使用 1.malloc函数 (1)malloc的定义 (2)malloc函数的注意事项 (3)malloc函数的使用 2.calloc函数 (1)calloc函数的定义 (2)calloc函数的注意事项 (3)calloc函数的使用 3.realloc函数 (1)realloc函数的定义 (2)realloc函数的注意事项 (3)realloc函数的使用 总结 一.为什么存在动态内存分配 在c语言中我们目前掌握的内存开辟方式有: int val

  • C语言中动态内存管理图文详解

    目录 1.动态内存开辟的原因 2.动态内存函数的介绍 2.1malloc和free 2.2calloc 2.3realloc 3.常见的动态内存错误 3.1对NULL指针的解引用操作 3.2对动态开辟空间的越界访问 3.3对非动态开辟内存使用free 3.4使用释放一块动态开辟内存的一部分 3.5对同一块动态内存多次释放 3.6动态开辟内存忘记释放(内存泄漏) 4.练习 4.1练习1 4.1练习2 4.3练习3 4.4练习4 5.C/C++程序的内存开辟 总结 1.动态内存开辟的原因 常见的内存

  • 深入了解C语言的动态内存管理

    目录 一.为什么会存在动态内存 二.动态内存函数 1.malloc和free 2.calloc 3.realloc 三.动态内存函数常见错误 2.对NULL指针进行解引用操作 3.使用free释放一块动态开辟内存的一部分 4.对静态内存进行free释放 5.对同一内存空间多次释放 6.动态开辟空间忘记释放 四.经典笔试题 1.笔试1 2.笔试2 3.笔试3 总结 一.为什么会存在动态内存 int data=20;//在栈空间上开辟4个字节空间 char ch[5]={0};//在栈开辟5个字节连

  • 详解C语言中动态内存管理及柔性数组的使用

    目录 一.malloc 二.free 三.calloc 四.realloc 1.realloc在扩容时的情况 2.realloc也能实现malloc功能 五.使用动态内存的常见错误 1.free空指针 2.对动态开辟的空间越界访问 3.对非动态开辟内容free 4.只free动态开辟空间的一部分 5.对同一块内存多次free 6.动态内存空间忘记释放(内存泄漏) 六.柔性数组 1.柔性数组的概念 2.柔性数组的特点 3.柔性数组的使用场景 4.柔性数组的优点 一.malloc 这个函数向堆区申请

  • C语言的动态内存管理你了解吗

    目录 C/C++内存分配方式 C++内存管理方式 new和delete的使用 new和delete的骚操作 new和delete的区别 重载new和delete 定位new表达式 内存泄露 总结 C/C++内存分配方式 在学习C语言阶段的时候,创建一个变量,编译器会为它分配一块内存.而创建一个C++对象的时候,编译器会为这个对象分配内存,并且调用合适的构造函数进行初始化. 那么编译器的内存分配方式是怎样的呢? 内存分配可以有以下的几种方式 从静态存储区分配.这样的分配方式在程序开始前就可以为对象

  • C++动态内存管理详解

    目录 1.C/C++程序地址空间 2.C语言动态内存管理 (1)malloc (2)calloc (3)realloc (4)free 3.C++动态内存管理 (1)C++为什么要设计一套自己专属的动态内存管理方式? (2)new/delete定义 1)new/delete操作内置类型 2)new/delete操作自定义类型 (3)new/delete的实现原理 4.malloc/free和new/delete的区别 共同点: 不同点: 5.内存泄漏 总结 1.C/C++程序地址空间 计算机物理

  • 一文详解C++中动态内存管理

    目录 前言 1.C/C++程序的内存开辟 2.C语言中动态内存管理方式:malloc/calloc/realloc/free 2.1malloc.calloc.realloc区别? 3.C++内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 3.3new和malloc处理失败 4.operator new与operator delete函数 4.1 operator new与operator delete函数 4.1.1 我们看看operator

  • C语言动态内存管理的实现

    目录 1. 摘要 2. 为什么存在动态内存管理 3. 动态内存函数 3.1 malloc 3.2 free 3.3 calloc 3.4 realloc 4. 常见的动态内存错误 5. 几个经典笔试题 参考答案 6. 参考文献 1. 摘要 本文主要详解C语言中的动态内存分配 2. 为什么存在动态内存管理 我们先来看一段变量的声明: double x = 1.000000; char str[] = "abcdef"; 好的,上述变量的声明有何特点呢? 请思考一下,我的朋友. 对,没错,

随机推荐