一、C/C++中程序内存区域划分
内存区域相关作用:
- 栈又叫堆栈:非静态局部变量、函数参数、返回值等等,栈是向下增长的
- 内存映射段时高效的I/O映射方式,用于装载一个共享的动态内存库,用户可以使用系统接口创建共享共享内存,做进程间通信
- 堆用于程序运行时动态内存分配,堆时可以上增长的
- 数据段:存储全局数据和静态数据
- 代码段:可执行的代码、只读常量
在语法上将数据段称为静态区、代码段称为常量区,而以上操作系统的命名。
提出相关思考:
- 为什么要分不同的区域
- 哪个区域是我们需要重点关注的
回答:
- 根据对象不同的生命周期和作用域,分配到不同的区域中,统一管理,高效地对对象进行处理
- 堆是我们要需要重点关注的,这是系统留给我们控制的内存,其他系统是自动的
1.1 相关练习测试
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"; const 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); }
答案:
- 选择题:C、C、C、A、A 。A、A、A、D、A、B
- 填空题:40、5、4、4/8、4、4/8
这里容易混洗的char str1[] ="abcd"
与const char* str2 ="abcd"
。这里str1
是个数组将常量拷贝到数组,而str2
是直接指向常量区中常量。
二、C语言中动态内存管理方式
C语言中,系统通过一系列函数赋予了我们对堆上空间的控制
void Test () { int* p1 = (int*) malloc(sizeof(int)); free(p1); int* p2 = (int*)calloc(4, sizeof (int)); int* p3 = (int*)realloc(p2, sizeof(int)*10); free(p3 ); }
提出思考:
- malloc/calloc/realloc的区别是什么?
- 这里使用realloc是否还需要free(p2)
- malloc的实现原理?
第一个问题的回答:
对于malloc/calloc/realloc
是系统为我们提供在堆上申请空间的途径。在功能上大体是相同的,对于malloc
与calloc
这两个函数,除了参数部分及其是否完成初始化,其他功能是相同的。
relloc
比较特别,属于扩容时使用的函数。扩容有两种方式:原地扩容和异地扩容。如果realloc
第一个参数部分为空,可以当作malloc
使用)。具体还是参考下这篇博客有详细解释内存管理
第二个问题的回答:
由于realloc
进行了扩容操作。如果是原地扩容,在原来开辟空间上完成扩容操作,这里p3会同p2指向这块空间,只需要free(p3)
;如果是异地扩容,将p2空间中数据拷贝一份,在堆上找一块空间充足地方,完成扩容和拷贝操作,p2指向原空间,会被系统自动收回,不需要对p2进行free操作。对此无论是原地还是异地,只需要free(p3)即可
第三个问题的回答:
可以通过该链接进行学习GLibc堆利用入门
三、C++内存管理方式
在C++中,虽然可以继续使用C语言对于内存管理方式,但是在有些地方就无能为力,而且使用起来比较麻烦。对此因此C++又提出了自己的内存管理方式:通过new和delete操作符进行动态内存管理
3.1 使用new/delete进行数据操作
3.1.1 new/delete 操作内置类型
int main() { //动态申请一个int类型的空间 int* ptr1 = new int; //动态申请一个int类型的空间并且初始化为10 int* ptr2 = new int(10); //动态申请10个int类型的空间 int* ptr3 = new int[3]; //动态申请10个int类型的空间并且完成初始化 int* ptr4 = new int[10]{ 1,2,3 };//剩下没有明确给值,默认为0 delete ptr1; delete ptr2; delete []ptr3; delete[]ptr4; return 0; }
注意需要匹配使用new和delete操作符:
- 申请和释放单个元素的空间:new、delete
- 申请和释放多个元素的空间:new[]、delete[]
3.1.2 new和delete操作自定义类型
class A { public: A(int a = 0) :_a(a) { cout << "A():" << this << endl; } ~A() { cout << "~A():" << this << endl; } private: int _a; }; int main() { //自定义类型 A* p1 = (A*)malloc(sizeof(A)); A* p2 = new A(1); free(p1); delete p2; //内置类型 int* p3 = (int*)malloc(sizeof(int)); int* p4 = new int; free(p3); delete p4; //开辟连续自定义类型空间 A* p5 = (A*)malloc(sizeof(A) * 10); A* p6 = new A[10]; free(p5); delete[] p6; return 0; }
从结果上来看,对于new
与malloc
最大差别在于对自定义类型除了开辟空间以外,还会调用构造函数和析构函数及其进行良好的初始化和控制。对于malloc
而言无法对自定义类型进行好的初始化和控制,只负责开辟内存,除此之外内置类型几乎相同(初始化不同)
对于new优于malloc的几点:
- 用法上进行调正,更简洁好用
- 可以控制初始化
- 对于自定义类型,new可以开空间+构造函数
- new配合构造函数,可以更加便捷创建节点等
- new失败了以后抛异常,不需要手动检查
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)https://developer.aliyun.com/article/1617322