实例共8个变量,包括一个全局变量g_nGlobal,1个局部静态变量nLocalStatic、以及6个局部变量。
在Windows XP 中使用VC2008编译运行,程序出处如下:
globalvariable:0x417000 staticvariable:0x417148 localprinter1:0x4158fc localconstprinter:0x4158ec new:0x383248 malloc:0x383288 localprinter(pIntNew):0x12ff30 localprinter(pLocalString1):0x12ff60 localprinter(pLocalString2):0x12ff54 localvariable(nLocal):0x12ff48 localprinter(pMalloc):0x12ff24 localconstvariable(nLocalConst):0x12ff3c |
从输出结果可以看出程序数据的分配情况。
通过new分配的位于堆上,5个int共20个字节,由于在堆上位16字节对齐,所以占用了32个字节从(48~88),内存对齐可是加速CPU对数据的访问,但是同时也造成了空间浪费,C/C++中的class、union和struct,可以通过#pragma pack()或者配置编译器来实现按要求对齐或者取消对齐。
程序的内存分配如下图:
全局/静态数据区 栈 堆
g_nGlobal pIntNew-->int[0..4]
nLocalStatic pMalloc--->char
… ….
"LocalString1"?-------pLocalString1
"LocalString2"?-------pLocalString2
nLocalConst
全局/静态数据区是在程序编译阶段都已经分配好的,在整个程序运行过程中始终存在,用来保存全局变量、静态变量、常量等。
其中字符串常量存储区域的数据是不可以修改的
例如
char *pLocalString1 = "LocalString1";
pLocalString1[1]= 'a';//试图修改不可修改的内存
二 堆和栈
虽然平常都是"堆栈堆栈"连着说的,但是他们的定义和作用是有区别的
在C/C++中,一个函数的内部变量以及传递给函数的参数等都是存储在栈中的。当退出这些变量的作用域时,这些栈的内容会被弹出释放。而是用malloc或者new申请的内存位于堆中,不会随着变量作用域的结束而自动释放,从而产生内存泄露。
例如实例中的
int *pIntNew = new int[5];
char *pMalloc = (char *)malloc(1);
pIntNew和pMalloc两个变量本身是位于栈的,当main()结束退出时,会被自动释放,而他们指向的内存在堆上,是不会自动释放的,因此造成内存泄露,所以必须显示的调用free或者delete。
这里产生一个问题:既然栈上的内存会自动释放不存在泄漏问题,而堆必须显示释放容易产生内存泄漏,为什么还要是用堆呢?
这是因为很多应用需要动态的管理数据,例如链表,而当需要新增节点插入链表时,此时就需要在堆上申请内存并插入节点。而且栈的大小也是有限制的,占用内存较多的对象只能在堆上分配。
除了以上的差别外,他们在大小和效率方面需要注意:
1、 大小
通常一个程序可以使用的栈的大小是固定的,由编译器决定。例如vc2008栈的默认大小就是1MB,当然可以修改它的大小,但是通常都不会很大。
int buf[1024*1024];//运行时会出错,栈溢出
但是堆的大小要比栈大很多,它主要受限与系统的虚拟内存的大小,可以分配比较大的数据。
2、效率
栈上的内存是系统自动分配的,pop与push都用相应的指令操作,因此效率比较高,而且分配的都是练习的内存空间,不会产生碎片。而堆上的内存是程序动态申请和释放的,系统需要通过一定的算法在堆空间中寻找合适的空间再进行分配,并修改相应的维护堆空间的链表,再返回地址给程序,因此效率比栈低,而且容易产生碎片。