C/C++内存管理
- 一、 C/C++内存分布
- 二、C语言中动态内存管理方式malloc/calloc/realloc/free
- 1.malloc
- 2.free
- 3. calloc
- 4. realloc
- 三、C++内存管理方式
- 3.1 new/delete操作内置类型
- 3.2 new和delete操作自定义类型
- 四、operator new与operator delete函数
- 五、new和delete的实现原理
- 5.1 内置类型
- 5.2 自定义类型
一、 C/C++内存分布
- 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。
- 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信。(Linux课程如果没学到这块,现在只需要了解一下)
- 堆用于程序运行时动态内存分配,堆是可以上增长的。
- 数据段–存储全局数据和静态数据。
- 代码段–可执行的代码/只读常量。
二、C语言中动态内存管理方式malloc/calloc/realloc/free
1.malloc
C语言
提供了一个动态内存开辟的函数:
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
• 如果开辟成功,则返回一个指向开辟好空间的指针。
• 如果开辟失败,则返回一个NULL
指针,因此malloc
的返回值一定要做检查。
•返回值的类型是void*
,所以malloc
函数并不知道开辟空间的类型,具体在使用的时候使⽤者自己来决定。
•如果参数size
为0
,malloc
的行为是标准是未定义的,取决于编译器。
2.free
C语言
提供了另外一个函数free
,专门是用来做动态内存的释放和回收的,函数原型如下:
void free (void* ptr);
free函数用来释放动态开辟的内存。
• 如果参数
ptr
指向的空间不是动态开辟的,那free
函数的行为是未定义的。
• 如果参数ptr
是NULL
指针,则函数什么事都不做。
举个例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num = 0;
scanf("%d", &num);
int arr[num] = {0};
int* ptr = NULL;
ptr = (int*)malloc(num*sizeof(int));
if(NULL != ptr)//判断ptr指针是否为空
{
int i = 0;
for(i=0; i<num; i++)
{
*(ptr+i) = 0;
}
}
free(ptr);//释放ptr所指向的动态内存
ptr = NULL;//一定要置空,避免野指针
return 0;
}
3. calloc
C语言
还提供了一个函数叫 calloc
,calloc
函数也用来动态内存分配。原型如下:
void* calloc (size_t num, size_t size);
• 函数的功能是为
num
个大小为size
的元素开辟一块空间,并且把空间的每个字节初始化为0
。
• 与函数malloc
的区别只在于calloc
会在返回地址之前把申请的空间的每个字节初始化为全0
举个例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = (int*)calloc(10, sizeof(int));
if(NULL != p)
{
int i = 0;
for(i=0; i<10; i++)
{
printf("%d ", *(p+i));
}
}
free(p);
p = NULL;
return 0;
}
//输出结果:
//0 0 0 0 0 0 0 0 0 0
4. realloc
•
realloc
函数的出现让动态内存管理更加灵活。
• 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那realloc
函数就可以做到对动态开辟内存大小的调整。
函数原型如下:
void* realloc (void* ptr, size_t size);
•
ptr
是要调整的内存地址
•size
调整之后新大小
• 返回值为调整之后的内存起始位置。
• 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
•realloc
在调整内存空间的是存在两种情况:
◦情况1:原有空间之后有足够大的空间
◦情况2:原有空间之后没有足够大的空间
三、C++内存管理方式
C语言
内存管理方式在C++
中可以继续使用,但有些地方就无能为力,而且使用起来比较麻烦,因此C++
又提出了自己的内存管理方式:通过new
和delete
操作符进行动态内存管理。
3.1 new/delete操作内置类型
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
注意:申请和释放单个元素的空间,使用new
和delete
操作符,申请和释放连续的空间,使用new[]
和delete[]
。
注意:匹配起来使用。
3.2 new和delete操作自定义类型
class A
{
public:
A(int a = 0)
: _a(a)
{
cout << "A():" << this << endl;
}
~A()
{
cout << "~A():" << this << endl;
}
private:
int _a;
};
int main()
{
// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
还会调用构造函数和析构函数
A* p1 = (A*)malloc(sizeof(A));
A* p2 = new A(1);
free(p1);
delete p2;
// 内置类型是几乎是一样的
int* p3 = (int*)malloc(sizeof(int)); // C
int* p4 = new int;
free(p3);
delete p4;
A* p5 = (A*)malloc(sizeof(A)*10);
A* p6 = new A[10];
free(p5);
delete[] p6;
return 0;
}
注意:在申请自定义类型的空间时,new
会调用构造函数,delete
会调用析构函数,而malloc
与free
不会。
四、operator new与operator delete函数
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;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
通过上述两个全局函数的实现知道,operator new
实际也是通过malloc
来申请空间,如果malloc
申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。operator delete
最终是通过free
来释放空间的。
五、new和delete的实现原理
5.1 内置类型
如果申请的是内置类型的空间,
new
和malloc
,delete
和free
基本类似,不同的地方是:new/delete
申请和释放的是单个元素的空间,new[]
和delete[]
申请的是连续空间,而且new
在申请空间失败时会抛异常,malloc
会返回NULL
。
5.2 自定义类型
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
来释放空间