1.栈又叫堆栈,非静态局部变量/函数参数/返回值等,栈是向下增长的。
2.内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享内存,做进程间通信。
3.堆用于程序运行时动态内存分配,堆是向上增长的
4.数据段———存储全局数据和静态数据
5.代码段———可执行的代码/只读常量
malloc / calloc /realloc 的区别
常见的内存泄漏:
void MemoryLeaks()
{
// 1、内存申请了忘记释放
int *pTest = (int *)malloc(10*sizeof( int));
assert(NULL != pTest);
DoSomething();
//free(pTest); 忘记了释放!!!
// 2、程序逻辑不清,以为释放了,实际内存泄露
int *pTest1 = (int *)malloc(10*sizeof( int));
int *pTest2 = (int *)malloc(10*sizeof( int));
DoSomething();
pTest1 = pTest2;
free(pTest1);
free(pTest2);//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++;//pTest4的指向地址已经变更!!!
DoSomething();
free(pTest4);
}
如何检测内存泄漏
1.系统自带的工具
2.自己实现检测工具
3.第三方检测工具VLD
系统自带的:
#inlcude <ctrdbg.h>
void TestFunc()
{
int* p = (int*)malloc(sizeof(int));
}
int main()
{
TestFunc();
_CtrDumpMemoryLead();
return 0;
}
C++内存管理方式
new和delete运算符进行动态内存管理
- new/delelte动态管理对象
- new[]/delelte[]动态管理对象数组
void Test()
{
//动态分配4个字节(1个int)的空间单个数据
int *p1 = new int;
//动态分配4个字节(1个int)的空间并初始化为10
int *p2 = new int(10);
//动态分配40个字节的空间(一片连续的空间)
int *p3 = new int[10];
delete p1;
delete p2;
delete[] p3;
}
C库malloc/free等来动态管理内存,为什么C++还要定义new/delete运算符来动态管理空间?
malloc与free是C、C++语言的标准库函数,new/delete是C++的运算符,它们都用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,只用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译控制权限之内,不能够吧执行构造函数和析构函数的任务强加给malloc/free。
因此c++语言需要一个能够完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delelte.new/delete不是库函数而是运算符。
malloc只能分配动态内存,而new除了分配动态内存还能构造对象,free只能释放内存,而delete除了释放空间,还执行了析构函数。
void TestMemory()
{
int* p1 = (int*)malloc(sizeof(int));
int* p2 = (int*)malloc(sizeof(int));
delete p1;
delete[] p2; //崩溃(如果没有析构函数,就不会崩溃)
int* p3 = new int;
int* p4 = new int;
free(p3);
delete[] p4;//崩溃(如果没有析构函数,就不会崩溃)
int* p5 = new int[10];
int* p6 = new int[10];
free(p5); //崩溃(如果没有析构函数,就不会崩溃)
delete p6;//崩溃(如果没有析构函数,就不会崩溃)
}
class Test
{
public:
Test()
: _data(0)
{
cout<<"Test():"<<this<<endl;
}
~Test()
{
cout<<"~Test():"<<this<<endl;
}
private:
int _data;
};
*new
假设new T 1.申请空间 void* operator new(sizeof(T)); 1.调用malloc循环申请,直到申请成功 申请成功:返回 申请失败: 检测:有没有提供内存不足的对应措施 提供:继续malloc 没提供:抛出异常 2.调用构造函数
delete
1.调用析构函数
将指针所指向对象中的资源清理掉
2.释放空间
void operator delete(void* p)
依靠free
new T[N]
- void* operator new[](size_t count = N * sizeof(T) +4)
注意:上面那个4,有析构函数时才加。(这4个字节的内存里存的是申请空间的大小)
调用 operator new(count)
调malloc:循环申请空间
申请成功:返回
申请失败:检测空间不足应对措施
2.调用N次构造函数
delete [ ]
假设析构函数显示给出(即会多出四个字节,要调N次析构函数)
1.销毁对象
a.从空间的前四个字节中取析构函数的调用次数N
b.调用N次析构函数析构N个对象
2.释放空间
void oprator delete[](void* p)
operator delete(p)
free(p)
重要的事情说三遍:
注意:malloc/free new/delete new[]/delete[] 一定要成对使用!!!
注意:malloc/free new/delete new[]/delete[] 一定要成对使用!!!
注意:malloc/free new/delete new[]/delete[] 一定要成对使用!!!
自己把operator new 给出,就不会去调系统的。优先调类里面的,然后全局作用域里的,然后系统
定位new表达式
定位new表达式是在已分配的原始空间中调用构造函数初始化有个对象。
Test* pt = (Test*)malloc(sizeof(Test));
new(pt) Test;
内存池化,先开辟一大片空间,每次要构造对象时,在这一大片空间里取拿所需要的空间。
这样做的原因:
1.多次创建对象,要多次用new,就要多次频繁调用malloc,系统提供堆的分配算法,效率低
2.每一次malloc要多占36个字节
3.每一次malloc的空间是不连续的,会造成内存碎片