C/C++中程序内存区域划分
栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时 这些存储单元自动被释放。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。
堆区(heap):⼀般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
代码段:存放函数体(类成员函数和全局函数)的⼆进制代码。
堆只能动态分配不能静态分配。栈可以动态分配(alloca函数)也可静态分配
下面我们看一个例子巩固一下吧
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";//char2:栈 *char2:栈
const char* pChar3 = "abcd";//pChar3:栈 *pChar3:常量区
}
对于char2和*char2、pChar3和*pChar3的解释如下图
C语言的内存管理方式
malloc
函数原型:void* malloc (size_t size)
功能:这个函数向内存申请⼀块连续可用的空间,并返回指向这块空间的指针。
特征:
* 如果开辟成功,则返回⼀个指向开辟好空间的指针
* 如果开辟失败,则返回⼀个 NULL 指针
* 返回值的类型是 void*
* malloc开辟内存是以字节为单位的
calloc
函数原型:void* calloc (size_t num, size_t size)
功能:把num 个大小为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
特征:
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为0。
realloc
函数原型:void* realloc (void* ptr, size_t size)
功能:可以做到对动态开辟内存大小的调整
特征:
* ptr 是要调整的内存地址 ,size 调整之后新大小,返回值为调整之后的内存起始位置。
* 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
* realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有足够大的空间,此时内存就会直接扩大,ptr指向原来的内存。
情况2:原有空间之后没有足够大的空间,此时会找一个足够大的内存,把原来的数据拷贝过去,ptr指向改变。
free
函数原型:void free (void* ptr)
功能:用来释放动态开辟的内存。
特性:
* 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
* 如果参数 ptr 是NULL指针,则函数什么事都不做。
注意:释放后ptr变成野指针,需要置空。
C++的内存管理
new
功能:开辟指定类型空间。
用法:
* 开辟一个空间
new 类型 举例:new int
new 类型(初始值) 举例:new int(10)
* 开辟多个空间
new 类型[数量] 举例:new int[10]
new 类型[数量]{初始值} 举例:new int[10]{0}
特性:
* 如果开辟的是自定义类型,会调用相应构造函数。
* 开辟失败会抛异常而不是返回空指针。
定位new
功能:在已分配的原始内存空间中调用构造函数初始化一个对象。
用法:new (指针) 类型 (初始化)
使用场景
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
// 定位new/replacement new
int main()
{
// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没
有执行
A* p1 = (A*)malloc(sizeof(A));
new(p1)A; // 注意:如果A类的构造函数有参数时,此处需要传参
p1->~A();
free(p1);
A* p2 = (A*)operator new(sizeof(A));
new(p2)A(10);
p2->~A();
operator delete(p2);
return 0;
}
delete
功能:释放new开辟的内存
用法:delete 指针 delete[] 指针
特性:
* 如果释放的是自定义类型,会调用相应析构函数。
* 释放失败会抛异常,而不是报错。
注意:new开辟的空间必须由delete释放,new[]开辟的空间必须由delete释放。
* 解释一下为什么
在VS中当我们用new[]开辟空间时,会额外在头部开辟一个小空间,用来存放开辟了多少空间。我们获得的指针为这个小空间后面的部分。delete[]会自动跳转到这个小空间前面,此时指针指向总开辟的空间的头部,然后free。
由于总开辟的空间=小空间+存储数据的空间。所以如果我们用delete删除时只能free一部分的开辟空间,发生错误。
operator new与operator delete函数
operator new 和operator delete是 系统提供的全局函数。
new在底层调用operator new全局函数来申请空间。
delete在底层通过 operator delete全局函数来释放空间。
operator new 实际也是通过malloc来申请空间,malloc申请空间成功就直接返回,否则operator new通过检测空指针来确定是否抛异常。
operator delete 最终是通过free来释放空间的。如果释放失败会抛异常。
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
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: 该函数最终是通过free来释放空间的
*/
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的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
new和delete的实现原理
对于内置类型
new和malloc,delete和free基本类似,不同的地方是: new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返NULL。
对于自定义类型
new的原理
1. 调用operator new函数申请空间
2. 在申请的空间上执行构造函数
delete的原理
1. 在空间上执行析构函数
2. 调用operator delete函数释放对象的空间
new T[N]的原理
1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
2. 在申请的空间上执行N次构造函数
delete[]的原理
1. 在释放的对象空间上执行N次析构函数
2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间
malloc/free和new/delete的区别
1. malloc和free是函数,new和delete是操作符
2. malloc申请的空间不会初始化,new可以初始化
3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new 在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成 空间中资源的清理