一个C/C++源程序经过编译之后,其应用程序使用的内存可划分为一下几个部分:
1)代码区
代码区存放函数体的二进制代码。可执行文件加载之后,就存放在进程的代码区。这部分分区域是只读的,如果试图修改,将导致运行时错误。一般来说,程序的代码区存放的是程序的可执行代码,在某些特殊情况下,一些重要的数据也可以放入代码区,以防止错误修改。
2)栈区
程序中用来存放函数的参数值、局部变量、临时变量。由编译器生成的代码自动完成内存的分配与释放,操作类似于数据结构中的栈。windows下,栈是一块由高地址向低地址方向生长的空间。在程序运行时,只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常,提示栈溢出。除非特别指定,C++程序运行时,栈的最大容量是系统预先规定好的。
#include"pch.h"
#include<iomanip>
#include<iostream>
using namespace std;
int *addr;
int cnt;
//每次调用函数时,会把参数先压栈,所以使用 addr-&i 就可以知道一次递归调用耗费多少内存
void func(int i) {
cout << cnt << ":" << (int)addr - int(&i) << endl;
cnt++;
addr = &i;
func(i);
}
int main() {
int i;
addr = &i;
func(2);
}
运行结果:
内存溢出
所以一个进程可以使用的内存大小大概是:4756*216/1024 = 1003K,大约1M。
3)堆区
供程序员动态申请和释放空间的内存区。这个区域由程序员自己负责维护。
堆的具体实现和管理方式与操作系统相关,不同操作系统的实现细节是不同的。一般原理:在操作系统中设置一个记录空闲内存地址的链表,当系统收到程序的申请时,遍历该链表,寻找第一个空间大于申请空间的堆节点。然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。而且一般会在这块内存空间中的首地址处记录本次分配的大小,以便代码中的delete语句能够正确地释放本内存空间。由于找到的堆节点大小不一定等于申请的大小,系统会自动地将多余的那部分重新放入空闲链表中。
4)全局/静态区
用来存放全局变量和静态变量的内存区。程序在创建这块存储区时会自动将所有字节清0.所以如果没有显示地位全局(静态)变量赋初始值,那么它的初始值就是0.程序结束之后由系统释放。
5)常量区
用于存放字符串常量的内存区。程序运行结束后由系统自动释放。
常量区既不是存放文字常量(存放在代码区),也不是存放常变量(存放在栈区或堆区)的存储区。常量区里存放的是字符串常量,以及一些在程序运行中不能修改的重要数据(比如类的虚函数表等)。常量区是只读的,如果试图修改,将导致运行时错误。