内存分布对于操作系统来说就像是一个国家的地市级区域的划分 划分这些区域的目的就是为了方便进行管理 在一个程序中 不同类型的变量也存放在不同的区域中
在C/C++中,内存分成5个区,他们分别是栈、内存映射区 堆 数据段 代码段
在下面这例子中 这些变量和常量分别都存在内存中的什么地方呢?
int globalVar = 1;
static int staticGlobalVar = 1;
int main()
{
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);
return 0;
}
图解:
解释上图:
栈
:又叫做堆栈 存放非静态局部变量/函数参数/返回值等等 (栈是向下增长的)
内存映射段
: 是高效的I/O映射方式 用于装载一个共享的动态内存库 用户库可以直接使用系统接口创建共享内存 做进程间通信使用(了解即可)
堆
:用于程序运行时动态内存分配的(malloc/calloc/realloc等) (堆是向上增长的)
数据段
: 存放全局数据和静态数据
代码段
:存放可执行代码/只读常量 不允许被修改
了解了这些之后再来看一下栈为什么是向上增下增长的堆为什么是向上增长的
栈区使用栈的数据结构进行存储的 满足先进后出 从高地址到低地址
堆并没有栈一样的结构,也没有栈一样的先进后出。需要人为的对内存进行分配使用
堆是向上增长的 从低地址到高地址 分配的地址并不是连续的
malloc calloc realloc 都是c语言提供的库函数 分别和free(也是库函数)成对使用 分别从堆中申请空间和释放空间
malloc和calloc的区别只在于calloc会把申请到的空间全部字节初始化为0 而malloc不会
realloc则是在malloc和calloc申请的空间后继续开辟连续的空间也叫原地扩容(如果后面的空间被使用 那么会在栈区找到一个合适的连续空间来使用 也叫异地扩容函数返回新的内存地址)
在原地扩容时 p1和p2 是同一块地址空间 不能同时释放p1和p2 释放一个即可
异地扩容是 p3地址已经还给操作系统了 不能在对p3进行释放 只能释放新地址p4
free则是释放在堆区申请的空间将空间还给操作系统
在C++中可以继续使用C语言的内存管理方式 但是在一些地方用起来很麻烦 因此C++提出属于自己的内存管理方式
通过new和delete操作符进行动态内存管理
int main()
{
//动态申请一个int类型的空间
int* p1 = new int(1);
//动态申请一个char类型空间并初始化为a
char* p2 = new char('a');
//动态申请10个连续int类型的空间
int* p3 = new int[10];
//动态申请10个int类型的空间 并初始化为 0,1,2 ... 9
int* p4 = new int[10] {0,1,2,3,4,5,6,7,8,9};
//释放p1
delete p1;
//释放p2
delete p2;
//释放p3
delete[] p3;
//释放p4
delete[] p4;
return 0;
}
申请和释放单个元素的空间 使用new和delete操作符
申请和释放连续的空间 使用new[] 和 delete[]
new、 delete、 new[]、 delete[] 都是c++中的操作符
new/delete 和 malloc/free 最大的区别是 new/delete对于自定义类型而言 不仅开了空间 还会调用构造函数和析构函数
malloc/free
不会去调用构造函数和析构函数 仅仅只为变量开空间和释放空间
new/delete
new 会先开空间 然后调用构造函数
delete会先调用析构函数 然后释放空间
注意:这里的Date(2023) 不是匿名对象 是new 操作符指定的类型(Date)和初始化的值(2023)
在前面文章中介绍过operator关键字 他是c++重载操作符的关键字
int main()
{
int* p1 = (int*)malloc(sizeof(int));
free (p1);
int* p2 = new int;
delete p2;
int* p3 = (int*) operator new(sizeof(int));
operator delete (p3);
return 0;
}
对于内置类型来说 甚至可以写成下面这种方式
这种方式语法上没有问题 但是一般没人这样写 反人类的写法 更推荐互相匹配的写法
这种写法也仅仅是针对内置类型而言 对于自定义类型则不可以 因为new和delete会调用构造函数和析构函数 malloc和free不会调用
int main()
{
int* p1 = (int*)malloc(sizeof(int));
delete p1;
int* p2 = new int;
operator delete (p2);
int* p3 = (int*) operator new(sizeof(int));
free(p3);
return 0;
}
operaotr new 和operator delete 对于内置类型 也不会去调用构造函数和析构函数 仅仅只是去申请空间 和释放空间
与malloc和free的区别在于 处理失败的方式不同 malloc失败返回NULL 而operator new 申请失败会抛异常
对于内置类型 new和malloc,delete和free基本一样 不同的地方在于new/delete申请和释放的是单个元素空间
而new[] 和delete[]申请和释放的是连续的空间
new在申请空间时会抛异常 malloc会返回NULL
相同点是 :
都是从堆上申请空间 并且需要手动释放
不同点是: