C中的动态内存管理
1、C语言中使用malloc/calloc/realloc/free进行动态内存管理,malloc/calloc/realloc用来在堆上开辟空间,free将申请的空间释放掉。
1)malloc
原型:void * malloc(size_t size);
该函数将在堆上分配一个size byte大小的内存。它分配的单原完全按字节大小计算,因此如此分配N个单原的student_t,那么要这样实现:(stdent_t )malloc(N sizeof (student_t)); (可用memset初始化)
2)calloc
原型:void* calloc(size_t size, int count);
该函数解决了上面的函数的不足,它将分配count个size大小的单原,因此在便用此函数的时候就会很方便,比如对上面的例子就可以:(student_t *)calloc(sizeof(t_student), N)就可以了。这样使用就会很清晰的知道分配的内存是一种什么样的逻辑方式。(会将申请的内存空间初始化)
3)realloc
改变原有内存空间的大小,若不能改变,则会开辟一段新的内存,将原有内存的内容拷贝过去,不会对新开辟的空间进行初始化。
4)_alloc
使用_alloc在栈上动态开辟内存,栈上空间具有函数作用域,在函数结束后系统自动回收,不用用户管理。
注意:堆上的内存需要用户自己管理,也就是说用户动态malloc/calloc/realloc的空间,必须自己free掉,否则会造成内存泄漏。
2、常见的内存泄漏
void MemoryLeaks()
{
//1.内存申了忘记释放
int *pTest = (int *)malloc(10*sizeof(int));
assert(NULL != pTest);
Do();
//2.程序逻辑不清,以为释放了,实际内存泄漏
int *pTest1 = (int *)malloc(10*sizeof(int));
int *pTest2 = (int *)malloc(10*sizeof(int));
Do();
pTest1 = pTesr2;
free(pTest1);
free(pTest2);
//3.程序误操作,将堆破坏
char *pTest3 = (char *)malloc(5);
strcpy(pTest3,"Memory Leaks!");
free(pTest3);
//4.释放是传入的地址和申请时的地方不同
int *pTest4 = (int *)malloc(10*sizeof(int));
assert(NULL != pTest4);
pTest4[0] = 0;
pTest4++;
Do();
free(pTest4);
}
C++中的动态内存管理
1、C++中使用new和delete运算符进行动态内存管理。
【new作用】
调用operator new分配空间。
调用构造函数初始化对象。
【delete作用】
调用析构函数清理对象
调用operator delete释放空间
【new[]作用】
调用operator new分配空间。
调用N次构造函数分别初始化每个对象。
【delete[]作用】
调用N次析构函数清理对象。(N是new[ ]出来的)
调用operator delete释放空间
2、C++的其他内存管理接口(placement版本)
void * operator new (size_t size);
void operator delete (size_t size);
void * operator new [](size_t size);
void operator delete[] (size_t size);
1) operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法一
样。
2)他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
3)实际operator new和operator delete只是malloc和free的一层封装。
void Test()
{
int *p1 = new int; //动态分配4个字节(1个int)的空间单个数据
int *p2 = new int(2); //动态分配4个字节(1个int)的空间并初始化为2
int *p3 = new int[2]; //动态分配8个字节(2个int)的空间
delete p1;
delete p2;
delete[] p3;
}
注意:new和delete、new[ ]和delete[ ]要匹配使用,否则会造成内存泄漏甚至崩溃的问题。
栈:
非静态局部变量/函数参数/返回值等等,栈是向下增长的。
数据段:
存储全局数据和静态数据(包括局部静态变量)
堆:
程序运行时的动态内存分配,可以向上增长的。
代码段:
可执行的代码和只读常量。
3、malloc/free和new/delete的区别和联系
1)都是动态内存管理的入口。
2)malloc/free是C/C++的标准库函数,而new/delete是C++的操作符
3)malloc/free只是动态的分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数
4)malloc/free需要手动计算类型大小且返回值为void*,new/delete可以自己计算类型的大小,返回对应类型的指针。
class Array
{
public:
Array(size_t size = 10)
:_size(size)
, _a(0)
{
cout << "Array(size_t size)" << endl;
if (size > 0)
{
_a = new int[size];
}
}
~Array()
{
cout << "~Array()" << endl;
if (_a)
{
delete[]_a;
_a = 0;
_size = 0;
}
}
private:
int* _a;
size_t _size;
};
void Test()
{
//malloc/free函数只是动态的分配内存空间/释放空间。
Array* p1 = (Array*)malloc(sizeof(Array));
//new/delete操作符除了分配空间还会调用构造函数和析构函数来初始化和清理。
Array* p2 = new Array; //1个类类型
Array* p3 = new Array(20); //1个类类型并初始化为20
Array* p4 = new Array[10]; //10个类类型
free(p1);
delete p2;
delete p3;
delete[] p4;
}
int main()
{
Test();
//程序结果构造函数和析构函数被调用12次。
return 0;
}
4、定位new表达式
定位new表达式是在以分配的原始空间中调用构造函数初始化一个对象。
new(place_address) type;
new(place_address) type(initializer-list);
place_address必须是一个指针,initializer-list是初始化列表。
eg:
利用mallco/free和定位new表达式来模拟new/delete和new[]/delete[]:
class A
{
public:
A(int a = 2)
:_a(a)
{
cout << "A()" << endl;
}
void Print()
{
cout << _a << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
//分配1个A类型空间
A* pa = (A*)malloc(sizeof(A));
new(pa)A(1); //调用构造函数
pa->~A(); //调用析构函数
free(pa);
cout << endl;
//分配5个A类型的空间
A* pa1 = (A*)malloc(5 * sizeof(A));
for (int i = 0; i < 5; i++)
{
new(pa1 + i)A(i); //调用5次构造函数
}
for (int i = 0; i < 5; i++)
{
(pa1+i)->~A(); //调用5次析构函数
}
free(pa1);
return 0;
}
结果如下: