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";
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);
}
答案如下图所示:
有几点需要说明:
- 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
- 堆用于程序运行时动态内存分配,堆是向上增长的。
- 数据段用于存储全局数据和静态数据。
- 代码段用于可执行的代码/只读常量。
我们知道,在C语言中,动态内存管理方式有 malloc /calloc /realloc /free 四种
而在C++的内存管理方式中,C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理
new / delete 的使用
在C++中,new 和 delete 是用于动态内存分配和释放的操作符。它们提供了一种在运行时分配和释放内存的方法。
new 运算符:用于动态分配内存。它返回一个指向新分配的内存的指针。
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[10]; // 方括号里是对象个数
// 创建MyClass动态对象
MyClass* obj = new MyClass();
delete 运算符:delete 用于释放由 new 分配的内存。
delete ptr4; // 释放ptr4指向的内容
delete ptr5; // 释放ptr5指向的内容
delete[] ptr6; //释放ptr6指向的数组内存
delete obj; // 销毁对象
➡️注意事项:
- 使用 new 分配的内存必须使用 delete 释放,以避免内存泄漏。
- 申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new[]和delete[]。
- 不要多次删除同一块内存,也不要删除未分配的内存。
- 在释放内存之前,确保指针不为空( nullptr )。
对于自定义类型而言,new/delete 和 malloc/free 最大区别是 new/delete 对于【自定义类型】除了开空间还会调用构造函数和析构函数,而对于内置类型来说则几乎一样。
class A
{
public :
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// 自定义类型
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;
}
operator new 与 operator delete
在C++中 new 和 delete 是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new 在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
意思就是说:👉operator new 实际也是通过malloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
operator new 语法形式为:void* operator new(size_t size)
,其中 size
表示要分配的内存大小(以字节为单位)。
operator delete 语法形式为:void operator delete(void* ptr)
,其中 ptr
是指向要释放的内存块的指针。
new和delete的实现原理
内置类型
如果申请的是内置类型的空间,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来释放空间
new / delete 和 malloc / free的区别
new / delete 和 malloc / free 的共同点是:都是从堆上申请空间,并且需要用户手动释放。
new / delete | malloc / free | |
---|---|---|
语言 | C++ | C/C++ |
语法 | new type; | malloc(size); |
返回类型 | 指向对象类型的指针 | malloc的返回值为void* |
参数 | 要分配的内存类型和大小 | 要分配的内存大小 |
内存初始化 | 分配的内存块已初始化为 0 | 分配的内存块未初始化 |
异常处理 | 如果分配失败,则抛出 std::bad_alloc 异常 | 如果分配失败,则返回 NULL |
重载 | 可以重载 operator new 和 operator delete | 不能重载 malloc 和 free |
函数还是操作符? | 操作符 | 函数 |
申请自定义类型对象 | 申请空间后会调用构造函数,在释放空间前会调用析构函数 | 只会开辟空间,不会调用构造函数与析构函数 |