C/C++中程序区域由6部分组成:内核空间、栈、内存映射段、堆、数据段、代码段,如下图所示:
内存区域划分说明:
C语言提供了一个动态内存开辟的函数:
void* malloc (size_t size);
malloc函数的说明:
int main()
{
//利用malloc动态开辟数组a
int* a = (int*)malloc(sizeof(int) * 10);
//对于是否开辟成功进行检查
if (a == NULL)
{
printf("malloc fail\n");
exit(-1);
}
return 0;
}
C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的:
void free (void* ptr);
free函数的说明:
int main()
{
//利用malloc动态开辟数组a
int* a = (int*)malloc(sizeof(int) * 10);
//对于是否开辟成功进行检查
if (a == NULL)
{
printf("malloc fail\n");
exit(-1);
}
//释放动态开辟的数组a
free(a);
//将a指针置空
a = NULL;
return 0;
}
C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配:
void* calloc (size_t num, size_t size);
calloc函数的说明:
int main()
{
//用calloc动态开辟数组a
int* a = (int*)calloc(10, sizeof(int));
if (a == NULL)
{
printf("calloc fail\n");
exit(-1);
}
//查看a的初始化值
for (int i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
realloc函数能够做到对动态开辟内存大小进行调整:
void* realloc (void* ptr, size_t size);
realloc函数的说明:
int main()
{
int* a = (int*)malloc(sizeof(int) * 10);
if (a == NULL)
{
printf("malloc fail\n");
exit(-1);
}
//先用tmp数组来保存新开辟的空间,防止开辟失败导致原数组数据的丢失
int* tmp = (int*)realloc(a, sizeof(int) * 20);
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
a = tmp;
return 0;
}
C语言内存管理方式在C++中可以继续使用,但是有些地方就无能为力而且使用起来会比较麻烦,因此C++又提出了自己的内存管理方式:通过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操作符,申请和释放连续的空间,使用new[] 和delete[]
除了内置类型,new/delete在操作自定义类型时也非常的方便:
class Date
{
public:
Date(int year=1,int month=1,int day=1)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date* d1 = (Date*)malloc(sizeof(Date));
if (d1 == nullptr)
{
cout << "malloc fail" << endl;
exit(-1);
}
Date* d2 = new Date;
free(d1);
delete d2;
return 0;
}
我们通过调试可以发现,如果是用malloc开辟的动态内存,并不会完成初始化赋值,而用new开辟的动态内存会调用自定义类型的构造函数去完成初始化赋值
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc和free不会
new和delete是用户进行动态内存申请和释放的操作符,operator new和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
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;
}
通过上述两个全局函数的实现我们可以知道,operator new实际也是通过malloc来申请空间,如果malloc申请空间成功则直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete最终是通过free来释放空间的。
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
malloc/free和new/delete都是从堆上申请空间,并且需要用户手动释放,它们的不同点是: