当前位置: 首页 > 编程日记 > 正文

堆和栈浅析【转】

引用:

一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)—   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) —   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区  —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}


二、堆和栈的理论知识
2.1申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。


2.2
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

2.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。


2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

2.6存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:
#i nclude
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。


2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

堆和栈的联系与区别dd

   在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。

    首先,我们举一个例子:

    void f() { int* p=new int[5]; }

    这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:

    00401028   push        14h

    0040102A   call        operator new (00401060)

    0040102F   add         esp,4

    00401032   mov         dword ptr [ebp-8],eax

    00401035   mov         eax,dword ptr [ebp-8]

    00401038   mov         dword ptr [ebp-4],eax

    这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?澳,错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。

    好了,我们回到我们的主题:堆和栈究竟有什么区别?

    主要的区别由以下几点:

    1、管理方式不同;

    2、空间大小不同;

    3、能否产生碎片不同;

    4、生长方向不同;

    5、分配方式不同;

    6、分配效率不同;

    管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

    空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:   

    打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。

注意:reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

    碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

    生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

    分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

    分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

    从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

    虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

    无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的:)

转载于:https://www.cnblogs.com/myssh/archive/2009/07/08/1518892.html

相关文章:

JS-只能输入中文和英文

<span style"font-family:KaiTi_GB2312;">转自&#xff1a;<a target_blank href"http://www.cnblogs.com/liupeizhi/articles/2487472.html">http://www.cnblogs.com/liupeizhi/articles/2487472.html</a></span> </pre>&l…

SQL Server中读取XML文件的简单做法

SQL Server 2000使得以XML导出数据变得更加简单&#xff0c;但在SQL Server 2000中导入XML数据并对其进行处理则有些麻烦。本文介绍在SQL Server中读取XML文件的简单做法。SQL Server 2000使得以XML导出数据变得更加简单&#xff0c;但在SQL Server 2000中导入XML数据并对其进行…

Calling Oracle stored procedures from Microsoft.NET

摘自&#xff1a;http://www.c-sharpcorner.com/UploadFile/john_charles/CallingOraclestoredproceduresfromMicrosoftdotNET06222007142805PM/CallingOraclestoredproceduresfromMicrosoftdotNET.aspxIntroduction This article is intended to illustrate how to illustrate…

Https的底层原理

Http协议&#xff1a; 转载于:https://www.cnblogs.com/auldlangsynezh/p/10469587.html

【Linux笔记(002) 】-- centos7 文档操作基本命令

索引&#xff1a; 目录索引 一、cd -- ChangeDirectory a) 切换到 /DemoLM/ 文件夹 b) 回到用户 Home 根目录&#xff1a;是哪个账户登录的就会进入哪个用户的根目录 二、pwd -- PrintWorkingDirectory a) 查看当前工作目录 三、mkdir -- MakeDirectory a) 创建一个 /test/ 目录…

JDBC操作数据库实例

返回目录&#xff1a;《学生信息管理系统&#xff08;JavaJSP&#xff09;》 这里以JDBC操作MySQL数据库为例。 假设有一个名为test的数据库&#xff0c;里面有一张学生表&#xff0c;表名称为student&#xff0c;表结构如下&#xff1a; student表结构表中数据如下&#xff1…

面向JavaScript开发人员的Adobe AIR与Dreamweaver

入门教程&#xff0c;非常详细&#xff0c;CS4里面应该可以省略前面几步直接开发了。 Adobe AIR对于HTML/JavaScript应用程序与桌面的集成有着出色的支持&#xff0c;但除了所有附加功能之外&#xff0c;还需要一些其他工具和技术。这篇文章探讨了使用HTML/JavaScript的Web开发…

在数据显示页面增加按姓名查询功能

在上一章内容《将数据库中表格信息输出到页面上》的基础上&#xff0c;增加按姓名查询功能。 问&#xff1a;怎么在显示学生信息的页面增加按照姓名查询的功能&#xff1f; 答&#xff1a;在显示学生信息的页面&#xff0c;使用<form>标签为用户创建表单&#xff0c;表单…

Spring AOP的一些概念

切面&#xff08;Aspect&#xff09;&#xff1a; 一个关注点的模块化&#xff0c;这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 在Spring AOP中&#xff0c;切面可以使用通用类&#xff08;基于模式的风格&#xff09; 或者在普通类中…

有关于Matlab的regionprops函数的PixelIdxList和PixelList的一点解释

上一篇文章&#xff08;点击这里&#xff09;的最后一点说到了regionprops的相关参数的意思&#xff0c;但是总感觉不够明确 现在重新对PixelIdxList和PixelList的内容经过实验之后得到了点启发 1.首先用excel建立了一个如下的表格&#xff0c;然后用mat保存为mat的方式进行加载…

windows 系统无法启动windows event log 服务

windows 系统无法启动windows event log 服务 关键词&#xff1a;无法启动系统事件日志 尝试解决步骤 【1】权限&#xff1a;把如图中logsfile文件等都给local service 【2】把C:\Windows\System32\winevt\Logs下面的文件全部移走到其他文件夹&#xff0c;再启动服务试试看 【…

移动互联网漫谈(3)

1.1WIFI WIFI是无线局域网的一种&#xff0c;全称Wireless Fidelity&#xff0c;又称802.11b标准&#xff0c;它的最大优点就是传输速度较高&#xff0c;可以达到11Mbps&#xff0c;另外它的有效距离也很长&#xff0c;同时也与已有的各种802.11 DSSS设备兼容。今夏最流行的笔…

实现对学生表的删除操作

在上一章内容《数据显示页面》的基础上&#xff0c;增加删除超链接&#xff0c;实现删除功能&#xff1b; 修改内容&#xff1a; 在数据显示页面的表格中&#xff0c;增加一列&#xff0c;列名为“删除”&#xff0c;用来显示删除超链接&#xff1b;为表格的行标签&#xff08…

FRAME与IFRAME

FRAME与IFRAME框架概念 &#xff1a; 所谓框架便是网页画面分成几个框窗&#xff0c;同时取得多个 URL。只需要转载于:https://www.cnblogs.com/vibratea/archive/2009/07/24/1530098.html

react实现全选、取消全选和个别选择

react里面实现全选和取消全选&#xff0c;个别选择等操作&#xff0c;效果如下 代码&#xff1a; import React, {Component} from react export default class Demo extends React.Component{constructor(props,context){super(props,context);this.state {checklist:[{name:…

PAT1036:Boys vs Girls

1036. Boys vs Girls (25) 时间限制400 ms内存限制65536 kB代码长度限制16000 B判题程序Standard作者CHEN, YueThis time you are asked to tell the difference between the lowest grade of all the male students and the highest grade of all the female students. Input …

NERO7光雕功能

找到NERO 7 PREMIUM的这个版本&#xff1a;Nero-7.10.1.0_chs_trial.exe百度中搜这个文件就找到了&#xff0c;用下面的序列号&#xff1a;1C80-0000-19E5-MA2X-4004-9268-7320&#xff0c;再装上最新版的官方光雕程序&#xff08;集成了加深标签的插件&#xff09;&#xff1a…

hive函数 get_json_object的使用

hive提供了json的解析函数&#xff1a;get_json_object 使用方法 对于jsonArray&#xff08;json数组&#xff09;&#xff0c;如person表的xjson字段有数据&#xff1a; [{"name":"王二狗","sex":"男","age":"25"…

实现对学生信息的增加操作

上一篇博客&#xff1a;《实现对学生表的删除操作》返回目录&#xff1a;《学生信息管理系统&#xff08;JavaJSP&#xff09;》本篇博客将介绍如何实现学生表中学生信息的增加操作。 1、在test1模块的web目录下&#xff0c;新建一个stuAddForm.jsp文件&#xff08;文件内容如…

【BZOJ 3879】SvT

【链接】h在这里写链接 【题意】 给你一个长度为n的字符串以及m个询问。 每个询问询问你所给的一些后缀,所有任意两个后缀之间的lcp的总和; n<5*10^5 ∑t<3*10^6【题解】 按照这些后缀的rank值升序排 ->利用Sa数组 即输入一个x,x--; sort(a1,…

快速计算表达式树

前言 .NET 3.5中新增的表达式树&#xff08;Expression Tree&#xff09;特性&#xff0c;第一次在.NET平台中引入了“逻辑即数据”的概念。也就是说&#xff0c;我们可以在代码里使用高级语言的形式编写一段逻辑&#xff0c;但是这段逻辑最终会被保存为数据。正因为如此&#…

随手拈来尽是折劲额事体

昨天中午&#xff0c;justina同学请我去港丽吃饭&#xff0c;世界顿时美好了&#xff01; 猛地发现&#xff0c;港丽的酸菜鱼竟然非常好吃&#xff0c;除了价钱贵&#xff0c;基本没有缺点了。 吃饭的时候&#xff0c;看到两件有劲的事情&#xff0c;一件比一件更折劲&#xff…

06 面向对象之:反射,双下方法

一、反射 反射的概念是由Smith在1982年首次提出的&#xff0c;主要是指程序可以访问、检测和修改它本身状态或行为的一种能力&#xff08;自省&#xff09;。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象…

实现对学生信息的修改操作

返回目录&#xff1a;《学生信息管理系统&#xff08;JavaJSP&#xff09;》 本篇博客主要实现对学生信息的修改操作&#xff1b; 步骤1、在学生信息的显示页面&#xff08;即student.jsp页面&#xff09;中&#xff0c;在表格最后增加一列“修改”超链接&#xff0c;在<tr&…

UML用例图概要(转)

用例图主要用来图示化系统的主事件流程&#xff0c;它主要用来描述客户的需求&#xff0c;即用户希望系统具备的完成一定功能的动作&#xff0c;通俗地理解用例就是软件的功能模块&#xff0c;所以是设计系统分析阶段的起点&#xff0c;设计人员根据客户的需求来创建和解释用例…

Python之迭代器,生成器与装饰器

1》迭代器原理及使用&#xff1a; 1>原理&#xff1a; 迭代器是访问集合元素的一种方式&#xff0c;迭代器对象从集合的第一个元素开始访问&#xff0c;直到所有的元素被访问完结束&#xff1b;迭代器只能往前不会后退&#xff0c;不过这也没什 么&a…

像乔布斯一样演讲

当苹果公司CEO史蒂夫-乔布斯开始今年&#xff08;2008年1月&#xff09;的Macworld时&#xff0c;我们看到他的商业演讲&#xff08;以下简称&#xff1a;演讲&#xff09;水平更上了一层楼。所有人都希 望能够在演讲中能更加简明扼要&#xff0c;乔布斯做到了&#xff0c;并且…

UNICODE使用的一些知识和技巧

UNICODE宏和_UNICODE宏的关系 在windows编程中,经常要编译Unicode版本的程序,方法是工程文件的配置中加上UNICODE或者_UNICODE编译条件,那么到底是用哪一个呢? Jeffrey Richter在《Windows核心编程》中说,_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件.当编译源…

编程学习网站收集

目录 1. 菜鸟教程 1.1 Java 教程 1.2 HTML 教程 1.3 CSS 教程 1.4 JavaScript 教程 1.5 JSP 教程 1.6 Servlet 教程 1.7 jQuery 教程 1.8 AJAX 教程 1.9 MySQL 教程 2. 易百教程 3. w3school 在线教程 1. 菜鸟教程 菜鸟教程 (www.runoob.com) 提供了编程的基础技术…

js短路表达式

今天碰见个题目&#xff0c;感觉短路表达式很好用。 题目&#xff1a; 定义一个计算圆面积的函数area_of_circle()&#xff0c;它有两个参数&#xff1a;r: 表示圆的半径&#xff1b;pi: 表示π的值&#xff0c;如果不传&#xff0c;则默认3.14function area_of_circle(r, pi) …