5.C++内存分配
内存分配
内存分配示意图

- Unused Memory:用于程序代码块对齐
Read-only code segment:只读,存代码和一些其他的东西 - Read/Write data segment:
- .data:存初始化的全局变量和static变量,另外还有文字常量区,常量字符串就是放在这里,程序结束后有系统释放
- .bss:存未初始化的全局变量和static变量
- Heap:通过new和malloc由低到高分配,由delete或free手动释放或者程序结束自动释放
- Shared libraries:调用的库文件,位于堆和栈之间
- Stack:由高向低增长,和堆的增长方式相对,对不同的OS来说,栈的初始大小有规定,可以修改,目前默认一般为2M,由编译器自动分配释放
- Kernel virtual memory:用户不可见不能访问
内存分配说明
C/C++编译的程序所占用内存区域一般分为以下5个部分:
- 栈区(stack):由编译器自动分配和释放,用来存放函数的参数、局部变量等。其操作方式类似于数据结构中的栈。
- 堆区(heap):一般由程序员分配和释放(通过malloc/free、new/delete),若程序员没有释放,则程序结束时由操作系统回收。它与数据结构中的堆是两回事,分配方式类似于链表。
- 全局/静态区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和初始化的静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由操作系统回收。
- 文字常量区:存放常量值,如常量字符串等,不允许修改,程序结束后由操作系统回收。
- 程序代码区:存放函数体的二进制代码。
例子
1 | #include <stdlib.h> |
2 关于栈和堆的内存分配与编译执行过程的再说明
说明
- “编译器申请和释放栈内存空间” 并不是说,在编译阶段就完成了内存空间的申请和释放,而是在编译阶段生成了内存申请和释放的命令。通过这些内存申请释放的机器指令,在执行阶段具体地完成内存的申请和释放。编译器负责生成这些命令,所以是编译器负责栈空间的管理。
- “栈”是一种数据结构,通过机器指令实现,而不是一种内存空间结构。通过生成的指令,操作具体的地址,移动地址的指针,实现栈的计算。
- 所以在编译阶段到底做了什么呢?当然是
- 编译器自动生成了栈空间的申请和释放的机器指令,将指令的对象翻译为相对地址,实现栈的逻辑结构 。
- 用户实现了堆空间申请和释放的代码,编译器负责翻译为机器指令。但是没有相对地址,只有相对地址的指针 。
- 在实行阶段,将程序装入内存,对相对地址进行重定位。执行过程中,完成栈和堆的空间分配操作。
关于一个典型问题的说明
1 | #include<iostream> |
- 问题描述:如果在编译时进行内存分配,那么n、a、c的相对地址应该都是确定的,但是a的长度只能在运行时确定,那么a和c在编译的时候相对地址到底是多少呢?
- 解决方案:在《操作系统》中介绍,重定位主要有两种方式,静态重定位(装入的时候进行重定位)。动态重定位(执行过程中进行重定位)。使用动态重定位,在执行到int a[n]的时候,对c进行动态重定位,那么就可以解决问题了。这么说现在主流的操作系统、编译器都是动态重定位吗?大概是吧。
- intelligence提示不能使用变量初始化数组的长度,因为编译器无法确定相对地址。但是实际编译过程中并不会报错,貌似是编译器采用了动态重定位技术,或者其他的方法。对于这种问题一般有两种解决方法:
- 让编译器自己解决(虽然提示不可以,但是编译器动态重定位的话应该行)
- 使用动态内存分配。malloc、new在堆上分配空间,就不会出问题。
- 使用vector,本身就是在堆上分配空间。
相关章节
- 编译原理
- 操作系统—内存管理—程序的装入与链接—重定位
- C++—标准库—动态内存管理—new、delete
- C++—面试—C++编译过程详解、C++内存分配
https://estom.github.io/2021/03/03/C++/%E9%9D%A2%E8%AF%95/5.C++%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D/
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Estom的博客!










