C++中栈内存和堆内存
- 1. 一个由C/C++编译的程序占用的内存分为以下几个部分:
1.1 栈区(stack)
由编译器自动分配和释放,存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈。
1.2 堆区(heap)
一般由程序员分配和释放,若程序员没有释放,则可能在程序结束时由操作系统(OS)回收。注意它与数据结构中的堆是两回事,其分配方式倒是类似于链表。
1.3 全局区(静态区,static)
全局变量和静态变量都存放在这里, 初始化了的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量存放在相邻的另一块区域(BSS)。程序结束后由系统释放。
1.4. 文字常量区
常量字符串就是放在这里的。程序结束后由系统释放。
1.5 程序代码区
存放函数体的二进制代码。
1.7 举例程序
int a = 0; // 全局初始化区 char *p1; // 全局未初始化区 int main() { int b; // 栈 char s[] = "abc"; // 栈 char *p2; // 栈 char *p3 = "123456"; // 123456在常量区,p3在栈上 static int c = 0; // 全局(静态)初始化区 p2 = new char[20]; // 分配得来的10和20字节的区域就在堆区 strcpy_s(p2,20,"abcde"); // 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方 cout << (void*)p2 <<'\t'<< (void*)p3<<endl; return 0; }
2. 数据结构中的栈与堆:
栈:连续存储的数据结构,具有先进后出的性质。通常的操作有入栈(压栈)、出栈和栈顶元素。想要读取栈整那个的某元素,就需要将其之前的所有元素出栈才能完成,类似现实生活中的箱子。
堆:非连续的树形储存数据结构,每个节点有一个值,整棵树是经过排序的。特点是根节点的值最小(或最大),且根节点的两个子树也是一个堆。常用来实现优先队列,存取随意。
3. 内存中的栈区与堆区
一般说到的内存是RAM,计算机内存的大致划分如下:
栈内存:由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。若分配失败,则提示栈溢出错误。注意,const局部变量也储存在栈区内,栈区向地址减小的方向增长。
//测试栈内存 #include <iostream> int main() { int i = 10; //变量i储存在栈区中 const int i2 = 20; int i3 = 30; std::cout << &i << " " << &i2 << " " << &i3 << std::endl; return 0; }
测试输出为:
&i3 < &i2 < &i,证明地址是减小的。
堆内存:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则则导致内存泄露。
//测试堆内存和栈内存的区别 #include <iostream> int main() { int i = 10; //变量i储存在栈区中 char pc[] = "hello!"; //储存在栈区 const double cd = 99.2; //储存在栈区 static long si = 99; //si储存在可读写区,专门用来储存全局变量和静态变量的内存 int* pi = new int(100); //指针pi指向的内存是在 堆区,专门储存程序运行时分配的内存 std::cout << &i << " " << &pc << " " << &cd << " " << &si << " " << pi << std::endl; delete pi; //需程序员自己释放 return 0; }
测试输出:
运行多次发现pi所指向的地址是跳跃的;静态/全局变量&si储存在可读写去;前三个变量储存在栈中,由程序自动分配和销毁。
3.1 申请方式的比较
3.1.1 栈
由系统(编译器)自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间。
3.1.2 堆
需要程序员自己申请,并指明大小。
在C中使用malloc函数(释放时使用free函数),如p1 = (char *)malloc(10);
在C++中使用new运算符(释放时使用delete运算符),如p2 = new char[20];
但是注意p1和p2本身是在栈中的。
3.2 申请后系统的响应比较
3.2.1 栈
只要栈的剩余空间大于所申请空间,系统就会为程序分配内存,否则将报异常提示栈溢出。
3.2.2 堆
首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。对于大多数系统,会在这块内存空间中的首地址处记录本次分配的内存大小,这样,代码中的delete语句才能正确地释放该内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。
3.3 申请内存大小的限制
3.3.1 栈
在Windows下,栈是向低地址扩展的,是一块连续的内存区域。这句话的意思是,栈顶的地址和栈的最大容量是系统预先规定好的,在 Windows下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
3.3.2 堆
堆是向高地址扩展的,是不连续的内存区域。这是由于系统是用链表来存储空闲内存地址的,自然也就不连续,而且链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
posted on 2017-11-25 11:17 Vincent_syr 阅读(264) 评论(0) 编辑 收藏 举报