C语言动态内存管理(malloc/calloc/realloc/free)
int* p1 = (int*)malloc(sizeof(int));//malloc返回值类型为void*
int* p2 = (int*)calloc(1, sizeof(int));//动态分配并初始化为0
int* p3 = (int*)realloc(p1, sizeof(int)*2);//当p1位空时,相当于malloc
free(p2);
free(p3);
C++动态内存管理(new/delete/new[]/delete[])
int* p1 = new int;
int* p2 = new int(3);//动态分配并初始化
int* p3 = new int[3];//动态分配数组
delete p1;
delete p2;
delete[] p3;
一定要匹配使用,否则可能出现内存泄漏甚至崩溃的问题。
内存管理
1. 栈又叫堆栈,非静态局部变量/函数参数/返回值等,在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用。
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。
3. 堆用于程序运行时动态内存分配,堆是向上增长的。
4. 数据段-存储全局变量和静态变量。在以前的 C 语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过 void* 来访问和操纵,程序结束后由系统自行释放),在 C++ 里面没有这个区分了,他们共同占用同一块内存区。
5. 代码段-可执行的代码(命令)/只读常量。
深入理解C++动态内存管理
本质:
malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符。
对于用户自定义的对象而言,用maloc/free无法满足动态管理对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。(自定义类型调用,内置类型不调用)
malloc/free需要手动计算类型大小且返回值会void*。new/delete可自己计算类型的大小,返回对应类型的指针,并且失败是抛异常。
联系:
既然new/delete的功能完全覆盖了malloc/free,为什么C++还保留malloc/free呢?因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。
#include <iostream>
using namespace std;
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 ()
{
Array* p1 = (Array*) malloc(sizeof (Array));
Array* p2 = new Array;
Array* p3 = new Array(20);
Array* p4 = new Array[10];
free(p1 );
delete p2 ;
delete p3 ;
delete[] p4 ;
}
int main()
{
Test();
return 0;
}
C++中的其它内存管理接口
void * operator new (size_tsize);
void operator delete (size_tsize);
void * operator new [](size_tsize);
void operator delete[] (size_tsize);
operator new和operator delete是malloc和free的一层封装,用法和malloch和free一样。与operator函数不同,不是重载函数。
在使用上(通过调试可以查看调用其顺序)
new 1. 调用operator new分配空间2. 调用构造函数初始化对象。
delete 1. 调用析构函数清理对象2. 调用operator delete释放空间
new[N] 1. 调用operator new分配空间2. 调用N次构造函数分别初始化每个对象。
delete[] 1. 调用N次析构函数清理对象2. 调用operator delete释放空间。
模拟实现new[]/delete[]
定位new表达式:在已分配的原始内存空间中调用构造函数初始化一个对象。
new (place_address) type
new (place_address) type(initializer-list)
place_address必须是一个指针,initializer-list是类型的初始化列表。
#include <iostream>
using namespace std;
class AA
{
public:
AA()
:_a(0)
{
cout<<"AA()"<<endl;
}
~AA()
{
//
cout<<"~AA()"<<endl;
}
private:
int _a;
};
//实现 NEW_ARRAY 宏,模拟 new[] 申请数组
#define NEW_ARRAY(PTR, TYPE, N) \
{ \
PTR = (TYPE*)operator new(sizeof(TYPE)*N + 4); \
(*(int*)PTR) = N; \
PTR = (TYPE*)((char*)PTR + 4); \
for(size_t i = 0; i<N; ++i) \
new(PTR+i)TYPE; \
}
//实现 DELETE_ARRAY 宏,模拟 delete[] 释放数组
#define DELETE_ARRAY(PTR, TYPE) \
{ \
int N = *((int*)PTR - 1); \
for(int i = 0; i<N; ++i) \
PTR[i].~ TYPE(); \
PTR = (TYPE*)((int*)PTR - 1); \
operator delete(PTR); \
}
int main()
{
AA* p1;
NEW_ARRAY(p1, AA, 3);
DELETE_ARRAY(p1, AA);
return 0;
}
内存泄漏的检测
#include <iostream>
#include <crtdbg.h>
using namespace std;
int main()
{
//_CrtSetBreakAlloc(67);
int* p = new int[10];
_CrtDumpMemoryLeaks();
return 0;
}
在程序结束前可以用_CrtDumpMemoryLeaks();函数进行内存泄漏的检测。记得加上头文件crtdbg.h。使用F5查看是否存在内存泄漏。上面代码调试后如下
然后可以使用_CrtSetBreakAlloc(67);函数(在程序开始)进行内存泄漏的定位。