C/C++内存分布
先介绍C/C++中程序内存区域的分划
观察下列代码,分析各数据在内存中所处的位置
int i = 1; static int statici = 1; void test() { static int staticn = 1; int n = 1; int arr[10] = { 0 }; char ch[] = "crush"; const char* p = "crush"; int* ptr1 = (int*)malloc(sizeof(int) * 4); int* ptr2 = (int*)calloc(4, sizeof(int)); int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); free(ptr1); free(ptr3); }
A.栈 B.堆 C.数据段(静态区) D.代码段(常量区) i在哪里__ statici在哪里__ staticn在哪里__ n在哪里__ arr[]在哪里__ ch[]在哪里__ *ch在哪里__ p在哪里__ *p在哪里__ ptr1在哪里__ *ptr1在哪里__ sizeof(arr)=__ sizeof(ch)=__ strlen(ch)=__ sizeof(p)=__ strlen(p)=__ sizeof(ptr1)=__
栈:存储非静态局部变量,函数参数,返回值等
堆:程序运行时进行内存分配
数据段:存储全局数据和静态数据
代码段:可执行程序,只读常量
C语言中的动态内存管理方式
malloc/calloc/realloc 和 free
代码如下
void test() { int* ptr1 = (int*)malloc(sizeof(int) * 4); int* ptr2 = (int*)calloc(4, sizeof(int)); int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); free(ptr1); //free(ptr2) 这里需要free(ptr2)吗? free(ptr3); }
由于已经在ptr2的基础上进行了扩容,所以不需要对ptr2进行释放,否则就会发生错误
C++中动态内存管理
new / delete 操作内置类型
void test() { int* p1 = new int; //C++动态申请一个int类型的空间 int* p2 = (int*)malloc(sizeof(int)); //C动态申请一个int类型的空间 delete p1; free(p2); int* p3 = new int[10]; //C++动态申请10个int类型的空间 int* p4 = (int*)malloc(sizeof(int) * 10); //C动态申请10个int类型的空间 delete p3; free(p4); int* p5 = new int(1); //C++动态申请一个int类型的空间并初始化 int* p6 = new int[5]{ 1,2,3,4,5 }; //C++动态申请5个int类型的空间并初始化 delete p5; delete[] p6; }
new/delete 和 malloc/free 针对内置类型没有任何区别
void test() { //动态申请一个int类型的空间 int* p1 = new int; //动态申请一个int类型的空间并进行初始化 int* p2 = new int(1); //动态申请5个int类型的空间 int* p3 = new int[5]; delete p1; delete p2; delete[] p3; }
C++动态申请和释放空间时,需要配对,若是单个元素,使用new/delete;若是多个元素使用new[]/delete[]
new 和 delete 操作自定义类型
class M { public: M() :_data(1) { cout << "M()" << endl; } ~M() { cout << "~M()" << endl; } private: int _data; }; void test() { //C++动态申请一个M类型的空间 M* p1 = new M; //C动态申请一个M类型的空间 M* p2 = (M*)malloc(sizeof(M)); delete p1; free(p2); //C++动态申请5个M类型的空间 M* p3 = new M[5]; //C动态申请5个M类型的空间 M* p4 = (M*)malloc(sizeof(M) * 5); delete[] p3; delete p4; } int main() { M m; test(); return 0; }
在申请自定义类型的空间时,new会调用构造函数,delete也会调用析构函数;相反malloc 和 free 则不会
operator new 和 operator delete 函数
operator new 和 operator delete 函数
new / delete C++是进行动态内存申请和释放的操作符, operator new和 operator delete是系统提供的全局函数,换一种说法便是,new 申请空间是通过调用 operator new来实现的,同理 delete 释放空间也是通过调用operator delete来实现的
本质上,operator new/operator delete的用法与 malloc/free 的功能完全一样,不同点在于,处理动态申请失败的方式不同;malloc申请失败返回NULL,operator new申请失败则会抛异常
以下三种动态申请空间和释放的效果是一样的
void test() { int* p1 = new int; delete p1; int* p2 = (int*)operator new(sizeof(int)); operator delete(p2); int* p3 = (int*)malloc(sizeof(int)); free(p3); }
new 和 delete 的实现原理
内置类型
申请的是内置类型的空间,new/delete 与 malloc/free 的区别在上面已经结束过,这里便不在赘叙
自定义类型
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来释放空间
定位 new 表达式(placement-new)
定位new表达式是在已经分配的原始内存空间中调用构造函数初始化一个对象
使用格式:
new(place-address)type或者
new(place-address)type(initalizer-list)
使用场景
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定位表达式进行显示调用构造函数并进行初始化
class M { public: M(int m = 0) :_m(1) { cout << "M()" << endl; } ~M() { cout << "~M()" << endl; } private: int _m; }; void test() { //p现在指向的是与M对象大小相同的一段空间 //还未调用构造函数,所以指向的不算是对象 M* p = (M*)operator new(sizeof(M)); //定位new,显示调用构造函数并进行初始化已经申请的空间 new(p)M;//new(p)M(1); //显示调用析构函数 p->~M(); operator delete(p); }
常见面试题
malloc / free 和 new / delete 的区别
malloc/free 和 new/delete的共同点:都是从堆上申请空间,并且使用完之后需要进行释放
malloc 和 free是函数,new和delete是操作符
malloc申请的空间不进行初始化,new可以进行初始化
malloc申请空间时,需要手动计算空间大小并传递,new只需要在其后跟上空间的类型即可,如果多个对象,[]中指定对象个数即可
malloc的返回值是void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型
malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
申请自定义类型对象时,malloc/free 只会开辟空间,不会调用构造函数和析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理
内存泄漏
什么是内存泄漏,内存泄漏的危害
内存泄漏的概念:内存泄漏是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费
内存泄漏的危害:长期运行的程序出现泄漏,影响很大。