文章目录
1、C/C++内存分布

- 栈(Stack)(堆栈):存非静态局部变量/函数参数/返回值等 等,栈是向下增长的。
- 堆(Heap) :用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段:存储全局数据和静态数据。
- 代码段:可执行的代码/只读常量。
C语言中动态内存管理方式:malloc/calloc/realloc/free
需包含头文件 stdlib.h
malloc:在堆上申请空间(不初始化)。
返回值:
成功:返回 void 类型(通常需强转)空间的起始地址;
失败:返回空指针。
calloc:在堆上申请空间(初始化)。
返回值:与malloc相同。
realloc:对已申请的空间扩容(malloc/calloc)。
分原地扩容(已申请的空间后空间够)和异地扩容(已申请的空间后空间不够,将数据拷贝到新开的空间,释放原空间)。对空指针使用行为和malloc相同。
返回值:
成功:返回 void 类型的空间的起始地址。
失败:返回空指针,原数据仍在。
free:释放堆上申请的空间(需起始地址)。
只能对堆上已申请的空间释放,一次申请释放一次,对 NULL/nullptr 释放则不执行。无返回值
#include<stdlib.h>
int main()
{
//申请五个整型的空间
int* i = (int*)malloc(5 * sizeof(int));
//申请五个整型的空间并初始化
int ci = (int*)calloc(sizeof(int), 5);
//对i扩容为10个整型的空间
int* ri = (int*)realloc(i, 10 * sizeof(int));
//空间释放
free(ri);
free(ci);
return 0;
}
2、C++内存管理
在c++,在原本c的基础上引入了一套新的内存管理机制new/delete

可以粗略的认为:
new = malloc + 调用构造函数
delete = 调用析构函数 + free
注意:c++的机制并不能完全代替c的机制。
3、operator new与operator delete函数
operator new和operator delete是系统提供的全局函数,当调用new和delete,其实是分别在底层调用operator new和operator delete。
operator new返回失败要默认是抛出bad_alloc的异常,但我们可以自定义执行什么操作。
//捕获bad_alloc异常 (x86下)
int main()
{
int n = 0;
try {
while (1)
{
//每次申请1M
char* i = new char[1024 * 1024];
cout << n << endl;
n++;
}
}
catch (const exception& e) {
//打印bad_alloc
cout << e.what() << endl;
}
}

4、new和delete的实现原理
4、1 内置类型
new 与 delete 的行为和 malloc 与 free 的行为类似,在底层 new 调用 malloc ,delete 调用 free 。但new 申请一个空间,new [] 申请连续的空间,失败抛异常; delete 释放一个空间,delete[] 释放连续的空间。
4、2 自定义类型
new的原理
1、调用 operator new 函数,申请一个对象的空间。
2、调用一次 T 的构造函数。
new T[n]的原理
1、调用 operator new[] ,申请n个对象的空间。
2、调用n次 T 的构造函数。
delete的原理
1、调用一次 T 的析构函数。
2、 operator delete 函数,释放一个对象的空间。
delete[] 的原理
1、调用n次 T 的析构函数。
2、调用 operator delete[] 函数,释放n个对象的空间。
4、3 new和delete不匹配
要点:对类类型new[]开空间,会在返回地址前多开一个整型大小的空间做计数器,delete[]会获取计数器存的数值,调用对应次的析构。
//隐式默认成员函数
class A {
private:
int _a;
};
//显式默认成员函数
class B {
public:
B(int b = 1)
:_b(b)
{
cout << "B(int b)" << endl;
}
~B()
{
cout << "~B()" << endl;
}
private:
int _b;
};
int main()
{
//内置类型
//正常
//int* a1 = new int(1);
//int* a2 = new int(1);
//delete[] a1;
//delete[] a2;
//正常
//int* b1 = new int[5];
//int* b2 = new int[5];
//delete b1;
//delete b2;
//--------------------------------------------------
//自定义类型
//
//正常
//A* b1 = new A;
//A* b2 = new A;
//delete[] b1;
//delete[] b2;
//死循环(析构出错)
//析构b1时 b1空间前的垃圾值被delete[]获取,导致计数器存的值是垃圾值。
//B* b1 = new B;
//B* b2 = new B;
//delete[] b1;
//delete[] b2;
//正常
//A* b1 = new A[5];
//A* b2 = new A[5];
//delete b1;
//delete b2;
//报错(不在起始地址释放空间)
//b1前的计数器存申请空间的个数,看似开5个整型的空间,实则空间前多开一个整型大小的计数器,
//b1指向计数器之后的地址,delete时,不在申请空间的起始地址释放而报错。
//B* b1 = new B[5];
//B* b2 = new B[5];
//delete b1;
//delete b2;
return 0;
}
5、定位new表达式(placement-new)
定位new格式:
new(指针)构造函数
指针:指向已申请好的空间
可以粗略的认为 operator new 和 定位 new 实现new的开空间和构造的分离;调用析构和operator delete 实现delete的析构和释放的分离。operator new是构造内存池的核心。
内存池:为了提效,一次性申请大量空间,减少频繁申请。
class A {
public:
A(int a)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
//只开空间
A* a = (A*)operator new(sizeof(A));
//只初始化
new(a)A(1);
//析构
a->~A();
//只释放空间
operator delete(a);
return 0;
}
6、malloc/free和new/delete的区别总结
本质
malloc/free 是函数,new/delete 是操作数。
申请的空间
malloc 申请不初始化,new 可以初始化。对自定义类型,new还会调用构造。
malloc既可以申请单个空间,又可以申请连续的空间;new只能申请单个空间,申请连续的空间需用new[]。
malloc申请空间的大小需计算,new不需要。
返回值
malloc返回的是 void 类型的指针,通常需要强转,new返回的是指定的类型。
申请空间失败
malloc返回空指针,new抛异常。
空间释放
对自定类型delete会先调用他的析构,再释放;free直接释放。
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/2602_95531348/article/details/159693921



