程序员成长之旅——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);
}
1. 选择题:
选项 : A.栈 B.堆 C.数据段 D.代码段
globalVar在哪里?__C__ staticGlobalVar在哪里?_C___
staticVar在哪里?_C___ localVar在哪里?__A__
num1 在哪里?_A___
char2在哪里?__A__ * char2在哪里?A___
pChar3在哪里?_A___ * pChar3在哪里?__D__
ptr1在哪里?_A___ * ptr1在哪里?__B__
2. 填空题:
sizeof(num1) = __40__;
sizeof(char2) = __5__; strlen(char2) = __4__;
sizeof(pChar3) = _4___; strlen(pChar3) = _4___;
sizeof(ptr1) = __4__;
上面这个题要注意 * char2 和 * pChar3存放的区别
char2 是数组首元素地址,解引用的话就是将数组第一个元素(刚开始的话就是将常量区的数字带到数组中并初始化)所以也是在栈上。
pchar3是字符串首元素地址,解引用就是字符串首元素,而字符串在常量区,也就是代码段。
再根据一个图片了解一下
说明
- 栈又叫堆栈,非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段–存储全局数据和静态数据。
- 代码段–可执行的代码/只读常量。
C语言中动态内存管理方式
malloc/calloc/realloc和free
void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
// 1.malloc/calloc/realloc的区别是什么?
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?不需要
free(p3 );
}
C++中内存管理方式
C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力而且使用起来比较麻烦,因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理。
new/delete操作内置类型
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[10];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
new和delete操作自定义类型
class Test
{
public:
Test()
: _data(0)
{
cout << "Test():" << this << endl;
}
~Test()
{
cout << "~Test():" << this << endl;
}
private:
int _data;
};
void Test2()
{
// 申请单个Test类型的空间
Test* p1 = (Test*)malloc(sizeof(Test));
free(p1);
// 申请10个Test类型的空间
Test* p2 = (Test*)malloc(sizeof(Test) * 10);
free(p2);
}
void Test2()
{
// 申请单个Test类型的对象
Test* p1 = new Test;
delete p1;
// 申请10个Test类型的对象
Test* p2 = new Test[10];
delete[] p2;
}
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
operator new 和 operator delete
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
#include<iostream>
using namespace std;
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test():" << this << endl;
}
private:
int _data;
};
int main()
{
Test* pt = new Test;
new: 这是一个关键字
/*1.申请空间----->void* operator new(字节数)
循环使用malloc(size)申请空间
成功直接返回 失败空间不足 空间不足的应对措施(函数) --- 用户
2.调用构造函数*/
delete pt;
delete
/*1.先调用析构函数:释放对象中的资源
2.释放空间void operator delete(void*)--->free*/
Test* pt = new Test[10];
delete[] pt;
return 0;
}
operator new 这是一个函数
malloc申请空间
1.成功—>直接返回
2.失败—>执行用户提供的空间不足的应对措施:
----------a.用户未提供—抛异常
----------b.用户提供—执行—继续用malloc申请—到1循环下去—直到申请成功
new T[N]
1.申请空间 void* operator new—>void* operator new(字节数)—>malloc
2.调用N次构造函数
operator malloc 是通过malloc来申请空间的,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
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来释放空间