今天更新一下C++内存分布和管理的东西,这玩意就是考验你的基础扎不扎实
直接上图
上图右边这段最好是牢记
内核空间就不说了,咱不管
栈区:那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。函数调用建立栈帧,参数、函数中局部变量都存在栈帧中,栈是向下增长的,向下增长的意思是:从栈申请的内存地址会越来越小,在栈区当中是先使用高地址再使用低地址。
内存映射段也不管
堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。理论上而言,后malloc的内存地址比先malloc的要大,但是也不一定,但是不一定,因为有可能下一次申请的是之前其他空间释放回来的,这个区一般都很大,在32位系统下,可以占差不多3G的样子
数据段:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的(DATA段)和未初始化的(BSS段),在C++里面没有这个区分了,它们共同占用同一块内存区
代码段:可执行代码其实就是二进制指令,这里是存放二进制代码的。通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。 在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
看完了右边再来看左边,左边的代码如下:
下列变量分别存储在哪
- int globalVar = 1;//全局变量
- static int staticGlobalVar = 1;//静态数据
- void Test()
- {
- static int staticVar = 1;//静态数据
- int localVar = 1;//栈帧里面
- int num1[10] = {1, 2, 3, 4};//栈
- char char2[] = "abcd";
- char* pChar3 = "abcd";
- 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);
- }
globalVar是全局变量所以它在数据段
staticGlobalVar是静态变量所以它在数据段
staticVar是静态变量所以它在数据段
localVar创建在test函数栈帧中,它是局部变量,在栈区
num1是数组,在函数栈帧中创建,存储在栈区
char2是数组,在函数栈帧中创建,存储在栈区
*char2拿到的是字符a的地址,因为char2是数组,里面的元素也是存储在栈帧里面的,所以*char2也在栈区
pchar3是指针变量,存储在栈区,它指向字符串的首字符地址
*pchar3是常量字符a,常量字符串是存储在常量区的,就是在代码段
ptr1是指针变量,他存储在栈区
ptr1指向的内存空间是malloc出来的,所以*ptr1是在堆区
这里要回忆一下C语言中动态内存管理的知识:malloc realloc和calloc分别是什么?
calloc等价于malloc+memest(0),开空间+初始化为0
realloc是对malloc或calloc的空间进行扩容
接下来就引入C++中的内存管理,就是new和delete
注意:malloc和free是库函数,而new和delete只是操作符
- void Test()
- {
- //C语言 malloc等是库函数
- int* p1 = (int*)malloc(sizeof(int));
- free(p1);
-
- //C++ new/delete是操作符
- // 动态申请一个int类型的空间
- int* ptr4 = new int;
- // 动态申请一个int类型的空间并初始化为10
- int* ptr5 = new int(10);
- delete ptr4;
- delete ptr5;
- }
那么两者的区别是什么?
如果动态申请的对象是内置类型,用malloc和new没有区别,如果动态申请的对象是自定义类型,有区别,因为new和delete不仅仅会开空间/释放空间,还会调用构造函数和析构函数
这里注意一点在new数组的时候,delete一定要加[],否则可能出错
接下来还有一点要知道的是operator new和operator delete函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间
我们在new一个对象T时,其实编译器会这样做:
1、申请内存,调用operator new(底层其实是将malloc的封装实现)
2、调用构造函数
在delete时,编译器会这么做:
1、调用T的析构函数
2、调用operator delete(底层其实是将free的封装实现)
说完了调用,那么如果调用失败呢?
如果是malloc,就会返回NULL;
如果是new,就会抛一个异常出来
对于上述提到的自定义类型,new和delete的原理是什么呢?
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来释放空间
最后一点 malloc和new的使用就必然设计到内存泄漏的问题
内存泄漏:在堆上申请了的空间,在我们不用了以后也没有释放,就存在内存泄漏,因为你不用了,也没有还给系统,别人也用不了