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);
}
1. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____
staticGlobalVar在哪里?____
staticVar在哪里?____
localVar在哪里?____
num1 在哪里?____
char2在哪里?____
*char2在哪里?___
pChar3在哪里?____
*pChar3在哪里?____
ptr1在哪里?____
*ptr1在哪里?____
// C C C A A A A A D A B
C语言的动态内存管理方式
- malloc:申请一段空间,返回void*指针。
- calloc : 申请一段空间,并将其全部字节初始化成0, 返回void*指针。
- realloc:扩容已申请空间的大小,分为原地扩容和异地扩容,如果是异地扩容会把原来空间释放,拷贝到新的空间(稍微大一点的空间一般都是异地扩容)。
- free :释放申请的空间。
C++的内存管理
C++引入了类和对象,C语言传统的内存管理方式不适用对象空间的申请,C++引入了两个操作符new delete
- 对于传统的内置类型,这两个操作符的行为和malloc和free 几乎一致。
- 对于类对象,new会申请空间+调用构造函数,delete会调用析构函数+释放空间。
- new 和 delete的使用
int* ptr1 = new int; // 申请一个整形
int* ptr2 = new int[10]; // 申请10个整形
int* ptr3 = new int(10); // 申请一个整形初始化为10
int* ptr4 = new int[10] {1, 2, 3, 4}; // 申请4个整形,把前四个初始化成1 2 3 4 后面全是0
delete ptr1;
delete[] ptr2;
delete ptr3;
delete[] ptr4;
- C语言内存申请失败会返回空指针;C++内存申请失败会抛异常,如果一场到主函数还没有被捕获就会报错。
- 32位操作系统下,可以申请内存上限大概是2GB。
测试代码:
#include <iostream>
using namespace std;
class A
{
public:
A(int a = 0)
:_a(a)
{
//__a = (int*)malloc(sizeof(int) * 10);
__a = new int[10];
cout << "A():" << this << endl;
}
~A()
{
//free(__a);
delete[] __a;
__a = nullptr;
cout << "~A():" << this << endl;
}
private:
int _a;
int* __a;
};
int main()
{
//int* ptr1 = (int*)malloc(sizeof(int) * 4);
//cout << ptr1 << endl;
//int* ptr2 = (int*)realloc(ptr1, sizeof(int) * 10);
//cout << ptr2 << endl;
//free(ptr2); // 不需要freeptr1
//A a;
//
//int* ptr1 = new int; // 申请一个整形
//int* ptr2 = new int[10]; // 申请10个整形
//int* ptr3 = new int(10); // 申请一个整形初始化为10
//int* ptr4 = new int[10] {1, 2, 3, 4}; // 申请4个整形,把前四个初始化成1 2 3 4 后面全是0
//delete ptr1;
//delete[] ptr2;
//delete ptr3;
//delete[] ptr4;
size_t limit = 0;
int* ptr1 = nullptr;
do
{
//ptr1 = new int[10 * 1024 * 1024 / 4]; // 失败抛异常
ptr1 = (int*)malloc(10 * 1024 * 1024); // 失败返回空指针
if (ptr1) limit += 10 * 1024 * 1024;
} while (ptr1);
cout << limit / 1024 / 1024 << endl; // 1970 大概2GB
return 0;
}
operator new 和 operator delete函数
- new和delete是两个操作符,operator new 和 operator delete是两个函数。
- new和delete的实现转到汇编其实是调用构造和operator new以及调用析构和operator delete。
- operator new是用malloc实现的,作用是申请对应大小的空间。operator delete是用free实现的,作用是释放对应的空间。
new和delete的实现原理
- 内置类型:new和malloc,delete和free的功能基本一致;new[]
delete[] 是申请释放一段连续的空间。 - 自定义类型:new是调用operator new开辟空间,用构造函数初始化;delete是调用析构函数,用operator delete释放空间。
- new[N] 的原理:调用operator new[]完成对N个对象空间的申请,调用N次构造函数完成对N个对象的初始化。
- delete[]的原理:调用N次析构函数完成对N个对象中空间资源的释放,然后调用operate delete[]完成对N个对象空间释放。
- 需要注意的一点是如果显示写了析构函数new[N]会额外开辟一个字节的空间存储数组大小,这个字节是返回地址的前一个字节,由于malloc申请的空间不能分开释放,如果调用delete释放这段空间,编译器会认为前面没有空间从传入指针位置释放导致释放位置错误。
6. 定位new表达式(replacement-new)
当申请了一段空间后如果没有初始化就不能显式调用构造函数去初始化,这个时候就可以使用定位new初始化。
// new (place_address) type或者new (place_address) type(initializer-list)
// place_address必须是一个指针,initializer - list是类型的初始化列表
A* ptr = (A*)malloc(sizeof(A));
new(ptr)A(10);
使用场景:定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
7. malloc/free和new/delete的区别
- malloc和free是函数,new和delete是操作符
- malloc申请的空间不会初始化,new可以初始化
- malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
- malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
- malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
- 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放