1.内存区域划分
内存区域分为内核区 、栈区 、堆区 、静态区(数据段) 、常量区(代码段) 以及空间映射区
栈区:函数栈帧、函数的参数列表以及局部变量都存储在栈上;
堆区:用来动态申请的空间。程序员可以用malloc/free、new/delete来进行空间申请和释放。动态申请来的变量或者对象都在堆上;
静态区/数据段:用来存储全局变量或者静态变量;
常量区/代码段:用来存储常量字符串等常量数据或者是已经执行的代码,该区只读不能修改
内核空间以及映射区还没有学!!
2.C中动态内存的管理
C语言中通过malloc、calloc、realloc、free来进行动态内存的申请和释放
内存申请的四个函数需要包含头文件<stdlib.h>,在C++中将原先C语言中的头文件去掉了.h,并且在头部加上c。
例如:<stdlib.h> -> <cstdlib>
<math.h> -> <cmath>
在C++程序中,两种写法都可以。
int* p1 = (int*)malloc(sizeof(int));
//检查
int* p2 = (int*)calloc(1, sizeof(int));
//检查
int* p3 = (int*)realloc(p1, 8);
malloc的功能是分配空间,参数是需要分配的空间大小,单位是字节
calloc的功能是分配空间,并且将空间初始化为0,参数分别是需要申请的元素个数,单个元素的大小
realloc的功能是重新分配空间,参数分别为原空间,现需要分配的空间,单位是字节。特别的,当原空间为空时,realloc就相当于malloc
三者的返回值都是void*类型,使用时需要进行强转,且在申请之后都需要进行检查,指针不为空再使用。
//检查
if (p1 == NULL)
{
perror("malloc fail");
return;
}
在使用结束后要进行空间的释放,否则会造成内存泄露,并且将指针置为空,避免野指针
free(p1);
free(p2);
free(p3);
p1 = NULL;
p2 = NULL;
p3 = NULL;
3.C++中动态内存管理
3.1new/delete操作符
C++借助new和delete操作符来实现动态内存管理。
new申请空间的格式
//跟()表示对该空间进行初始化
//跟[ ]表示申请的是数组
//当申请的是数组时初始化要用花括号,可以只对其中的一部分进行初始化
new 类型+()+[]
//申请一个int类型的空间
int* p1 = new int;
//申请一个double类型的空间并初始化为1.0
double* p2 = new double(1.0);
//申请数组,大小为10个char
char* p3 = new char[10] {0};
delete释放空间的格式
//前者用来释放单个对象的空间,后者用来释放申请的数组空间
delete 变量名/delete []变量名
delete p1;
delete p2;
delete[] p3;
3.2new/delete操作自定义类型
new/delete在为自定义类型申请空间时会在申请空间之后和销毁空间之前分别调用其构造函数(完成初始化操作)和析构函数
这也是new/delete和malloc/free的区别
class A
{
public:
A(int a)
:_a(a)
{
std::cout << "A()" << std::endl;
}
~A()
{
std::cout << "~A()" << std::endl;
}
private:
int _a;
};
void func3()
{
A* p1 = (A*)malloc(sizeof(A));
//检查
free(p1);
A* p2 = new A(1);
delete p2;
}
用new申请对象数组时也可进行初始化
1.用已存在的对象进行初始化
2.用匿名对象初始化
3.利用隐式转化进行初始化
A a1(1);
A a2(2);
A a3(3);
A* p1 = new A[10]{ a1,a2,a3 };//利用已存在的对象进行初始化
A* p2 = new A[10]{A(1),A(2)};//利用匿名对象进行初始化
A* p3 = new A[10]{ 1,2,3 };//利用隐式类型转换
4.operator new与operator delete
new、delete是C++中的申请释放内存的操作符,而operator new 和operator delete是系统提供的全局函数,调用new和delete时,其会分别在底层调用operator new 和operator delete。
而operator new 和operato delete其实调用了malloc和_free_dbg
而free是一个宏函数,其调用的就是_free_dbg。
综上:new和delete底层就是malloc和free。
对于new []和delet[]来说,其调用的全局函数是 operator new []和operator delete[],而这两个又调用operator new 和operator delete。
所以new []和delet[]底层也调用malloc和free。
operator new:
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void* p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
operator delete :
void operator delete(void* pUserData)
{
_CrtMemBlockHeader* pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg(pUserData, pHead->nBlockUse);
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
free:
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
5.new/delete、new[]/delete[]的匹配
用new申请的对象用delete来销毁,用new[]申请的对象用delete[]来销毁
对于内置类型来说,交叉使用都不会有问题,但还是建议匹配使用
int* p1 = new int;
//free(p1);
//delete p1;
delete[]p1;
对于申请自定义类型的单个对象来说,如果自定义类型中有资源,不匹配使用会导致内存泄漏
如果申请的是多个对象,则必须匹配使用,否则会出错
B* p1 = new B[10];
//free(p1);出错
//delete p1;出错
delete[]p1;
之所以会出错是因为创建对象数组时,会在头部多创建4个字节用来存储对象的个数
如果不匹配使用,会直接从2位置开始释放空间,而申请的一整块空间是不可以从中间开始释放的。
6. new和delete的调用过程
6.1对内置类型
当new/delete、malloc/free用于内置类型的内存管理时,其效果类似。
new/delete申请的是单个元素的空间及释放
new[]/delete[]申请的是连续的空间及释放
malloc在内存申请失败时会返回NULL,而new申请内存失败会抛异常(try,catch,throw)。
6.2对自定义类型
new会先调用operator new申请空间,然后调用构造函数;delete会先调用析构函数完成资源释放,然后调用operator delet释放空间
new [N]会先调用operator new []申请空间,然后调用 N次构造函数;delete []会先调用N次析构函数,然后调用operator delete []释放该空间
7.定位new表达式(placement-new)
定位new表达式主要与内存池配合使用
内存池简单来说就是某个功能需要频繁的申请空间,操作系统为了加快申请效率,为该功能提前申请一大块空间作为内存池,以后该功能申请空间时从该内存池申请即可,释放的空间也还会内存池。
定位new的格式为:
new(指向指定内存的指针)类型
或者
new(指向指定内存的指针)类型 (初始化)
8.new/delete、malloc/free的异同
同:都需要从堆上申请空间,申请的空间需要用户手动释放;
不同:
- malloc/free是函数,new/delete是操作符
- malloc申请空间时需要指出所申请空间的大小,而new只需要指定类型即可
- malloc的返回值void*,需要进行强制类型转换,new不需要
- malloc申请失败返回NULL,new失败会抛异常
- malloc/free在为自定义类型申请空间时不会调用构造和析构,new/delete会调用对应的构造和析构