【C/C++】你不能错过的C/C++内存管理!

一、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";
 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);
}

内存中对应的位置:
在这里插入图片描述

  • 栈:存放函数的参数,局部变量,寄存器的信息 (函数结束,栈帧释放),栈向下 生长
    • Linux下默认栈空间大小:8192kb=8M
    • Windows下栈空间:1024kb=1M
  • 堆:动态内存申请,malloc,calloc,realloc,必须用free释放,堆向上生长
  • 数据段:全局变量,被static修饰的数据 (生命周期和程序一致,程序退出数据清空)
  • 代码段:代码和只读常量

为什么要对内存划分不同的区域?
数据类型不同,对数据的管理方式也不同,为了方便查找要划分不同区域

二、内存管理

2.1 C语言内存管理:

malloc、calloc、realloc

int* p1 = (int*)malloc(sizeof(int));		// 动态申请int空间大小,随机值
int* p2 = (int*)calloc(4, sizeof(int));		// 动态申请int空间大小,并赋值4
int* p3 = (int*)realloc(p2, sizeof(int) * 10);	//调整p2的空间大小为10个int大小

相同点:

  1. 返回值为void*,接收时必须强转
  2. 申请失败返回NULL,因此使用之前要判空

不同点:

  1. malloc(size):只是申请空间大小为size的内存
  2. calloc(num,size):申请空间大小为size,并赋值为data
  3. realloc(void* p, size):将p所指的空间大小调整到size大小(扩大或者缩小),若p为NULL则功能和malloc相似。如果p后面的空间大小不足size,则重新找一个空间够size大小,并拷贝旧空间内容,最后释放旧空间大小。

注意:malloc 申请的内存空间会比预计大一点,在对象模型中前面有32字节的属性信息(包括申请内存的大小等),后面有4字节的结束位置,防止越界。

2.2 C++内存管理:

为什么C++会新建立一个内存管理方式?
C++是面向对象程序设计,用malloc、calloc、realloc动态开辟的类空间大小,不会调用构造函数,因此不能称之为对象,调用free释放时,也不会调用析构函数清理对象内部资源,可能造成内存泄露

  1. 申请/释放 单个类型空间:new/delete
  2. 申请/释放 连续类型空间:new[]/delete[]

动态申请内置类型:

int* p1=new int(10);10初始化
int* p2=new int[10]{1,2,3,4,5,6,7,8,9,0}; 申请连续空间并初始化
delete p1;
delete[] p2;

动态申请自定义类型:

class Test
{
public:
    Test(int p = 0)
        :p_(p)
    {
        cout << "构造:" << this << endl;
    }
    ~Test()
    {
        cout << "析构:" << this << endl;
    }
private:
    int p_;
};

int main()
{
    Test* p1 = new Test(100);				//会调用构造函数(类内成员t被初始化成1),因此生成的为对象
    Test* p2 = (Test*)malloc(sizeof(Test));	//不会调用(类内成员t没有初始化),因此只能称为和类类型大小相同的堆空间
    delete p1;								//会调用析构函数,清理
    free(p2);								//只会释放开辟的空间
    return 0;
}

malloc和new的区别:

  1. new是C++中的关键字,malloc是库函数,因此使用前要引入头文件
  2. malloc申请/释放空间不会调用构造/析构函数,new会调用构造/析构函数
  3. malloc申请失败返回NULL,new由于内部实现,不存在返回空指针的情况
  4. malloc返回值为void*,new返回对应类型的指针,因此不需要强转接收

注意:
new/delete、new[]/delete[]、malloc\free必须匹配使用,否则会造成程序崩溃或者内存泄漏的情况

class Test
{
public:
    Test(int val = 0)
        :p_(new int(val))
    {
        cout << "构造:" << this << endl;
    }
    ~Test()
    {
        delete p_;
        cout << "析构:" << this << endl;
    }
private:
    int* p_;
};

int main()
{
    Test* p1 = (Test*)malloc(sizeof(Test));
    Test* p2 = new Test;
    Test* p3 = new Test[2];
    
    delete p1;      // 程序崩溃,malloc没有调用构造函数
    delete[] p1;    // 程序崩溃,malloc没有调用构造函数

    free(p2);       // 内存泄漏,free没有调用析构函数

    free(p3);       // 程序崩溃,连续空间只释放了一部分
    delete p3;      // 程序崩溃,连续空间只释放了一部分
    return 0;
}

三、new/delete的工作流程

3.1 new

  • 1.申请堆空间
    • 调用void* operator new(size)函数申请空间,size是类的空间大小,内部循环调用malloc申请空间
      • 成功:返回空间首地址
      • 失败:调用_callnewh(size)函数,调用用户提供的解决方法,若失败则抛出bad_alloc类型异常,继续申请空间
  • 2.调用构造函数对申请的空间进行初始化

小结:由于operator new()函数的执行方式,new不会返回空

3.2 delete

  • 1.调用析构函数,清理对象中的资源
  • 2.调用operator delete()函数,内部调用free释放空间

3.3 new[]

  • 1.申请空间,调用void* operator new[] (size),其内部调用了operator new()函数申请N个类空间
  • 2.调用N次构造函数,初始化N个对象

3.4 delete[]

  • 1.调用N次析构函数对p所指向的空间资源进行清理
  • 2.调用void operator delete[] (void* p) 对p所指向的空间进行释放 —>内部调用了operator delete函数

四、将malloc开辟的类空间转换成对象

  • 用malloc 申请出来的空间不能称之为对象,因为没有调用构造函数
  • 定位new表达式:在已开辟好的堆空间中调用构造函数,初始化类空间,使之成为对象
#include<iostream>
using namespace std;
class Test
{
public:
	Test(int t = 0)
		:_t(t), _p(new int)
	{
		cout << "Test(int):" << endl;
	}
	~Test()
	{
		delete _p;
		cout << "~Test():" << this << endl;
	}
private:
	int _t;
	int* _p;
};
int main()
{
	Test* pt = (Test*)malloc(sizeof(Test));
	new(pt) Test(100);		// 定位new表达式
	pt->~Test();			// 调用析构函数清理资源
	free(pt);				// 调用free释放空间
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值