对于堆栈、静态、动态内存的理解

发布者:电子科技爱好者最新更新时间:2018-10-10 来源: eefocus关键字:堆栈  静态  动态内存 手机看文章 扫描二维码
随时随地手机看文章

预备知识—程序的内存分配

一个由C/C++编译的程序占用的内存分为以下几个部分

栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放

文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放

程序代码区—存放函数体的二进制代码。

一个正常的程序在内存中通常分为程序段、数据端、堆栈三部分。程序段里放着程序的机器码、只读数据,这个段通常是只读,对它的写操作是非法的。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。

在内存中,它们的位置如下:

+------------------+ 内存低端

| 程序段 |

|------------------|

| 数据段 |

|------------------|

| 堆栈 |

+------------------+ 内存高端

堆栈是内存中的一个连续的块。一个叫堆栈指针的寄存器(SP)指向堆栈的栈顶。堆栈的底部是一个固定地址。堆栈有一个特点就是,后进先出。也就是说,后放入的数据第一个取出。它支持两个操作,PUSH和POP。PUSH是将数据放到栈的顶端,POP是将栈顶的数据取出。

在高级语言中,程序函数调用、函数中的临时变量都用到堆栈。为什么呢?因为在调用一个函数时,我们需要对当前的操作进行保护,也为了函数执行后,程序可以正确的找到地方继续执行,所以参数的传递和返回值也用到了堆栈。通常对局部变量的引用是通过给出它们对SP的偏移量来实现的。另外还有一个基址指针(FP,在Intel芯片中是BP),许多编译器实际上是用它来引用本地变量和参数的。通常,参数的相对FP的偏移是正的,局部变量是负的。

当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;然后保存指令寄存器(IP)中的内容,做为返回地址(RET);第三个放入堆栈的是基址寄存器(FP);然后把当前的栈指针(SP)拷贝到FP,做为新的基地址;最后为本地变量留出一定空间,把SP减去适当的数值。

在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用;在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。

对比:

1 性能

栈:栈存在于RAM中。栈是动态的,它的存储速度是第二快的。stack

堆:堆位于RAM中,是一个通用的内存池。所有的对象都存储在堆中。heap

2 申请方式

stack【栈】: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 。

heap【堆】: 需要程序员自己申请,并指明大小,在c中malloc函数 如p1 = (char *)malloc(10); 在C++中用new运算符 如p2 = (char *)malloc(10); 但是注意:p1、p2本身是在栈中的。

3 申请后系统的响应

栈【stack】:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

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

4 申请大小的限制

栈【stack】:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆【heap】:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

5 申请效率的比较

栈【stack】:由系统自动分配,速度较快。但程序员是无法控制的。

堆【heap】:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.

另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。

6 堆和栈中的存储内容

栈【stack】:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆【heap】:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。

7 存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa";

char *s2 = "bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在运行时刻赋值的; 而bbbbbbbbbbb是在编译时就确定的; 但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

比如:

#include

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读取字符,显然慢了。

小结:

堆和栈的区别可以用如下的比喻来看出:

使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。


关键字:堆栈  静态  动态内存 引用地址:对于堆栈、静态、动态内存的理解

上一篇:高手分享一些对单片机的经验理解
下一篇:单片机各大分类与详解

推荐阅读最新更新时间:2024-03-16 16:15

Synopsys发布Coverity 8.5 静态分析工具
美国加利福尼亚州山景城,2016年7月11日 亮点: 为Ruby和node.js网络应用及Android移动应用引入Coverity静态分析功能 扩展Coverity安全分析功能,以减少更大范围的安全漏洞 增加对用于汽车、医疗设备和其他安全关键性行业的MISRA C 2012编码指南的全面支持 强化开发生命周期集成和报告,包括整合软件完整性平台上多个工具的报告功能 强化Coverity用户界面和文档的简体中文本地化 新思科技(Synopsys, Inc.,纳斯达克股票市场代码:SNPS)日前宣布发布Coverity 8.5,它是Synopsys公司行业领先的静态分析工具,也是其软件完整性平台
[模拟电子]
Linear推出低静态电流单片反激式稳压器
 凌力尔特公司近日推出低静态电流单片反激式稳压器 LT8300,该器件极大地简化了隔离型 DC/DC 转换器的设计。因为输出电压是从主端反激信号中检测的,所以无需光耦合器、第三个绕组或信号变压器实现反馈。LT8300 在 6V 至 100V 的输入电压范围内工作,具备一个 260mA、150V 的电源开关,提供高达 2W 的输出功率,非常适用于种类繁多的电信、数据通信、汽车、工业和医疗应用。 LT8300 以边界模式、电流模式控制的开关电路工作,从而在整个电压、负载和温度范围内实现了 ±5% 的调节。边界模式又称为临界导通模式 (CCM),与等效的连续导通模式设计相比,允许使用更小的变压器。低输出纹波突发模式 (Burst M
[电源管理]
Linear推出低<font color='red'>静态</font>电流单片反激式稳压器
【STM32H7教程】第27章 STM32H7的TCM,SRAM等动态内存分配实现
27.1 初学者重要提示 学习本章节前,务必优先学习第25章,了解TCM,SRAM等五块内存区的基础知识,比较重要。 将RTX5系统的动态内存管理整理了出来,可以同时管理多个分区。如果其它RTOS中使用,记得做互斥保护或者加个调度锁均可。 支持动态内存使用情况统计。 27.2 动态内存管理移植 移植比较简单,仅需添加两个文件到工程即可。 27.2.1 MDK版的移植 第1步,添加如下两个文件到MDK中 注,以本章配套例子为例,这两个文件的路径Usermalloc。 第2步,添加路径。 第3步,添加头文件。 如果哪个源文件要用到动态内存,包含rtx_lib.h即可,本章配套例子是直接将其放在了
[单片机]
【STM32H7教程】第27章 STM32H7的TCM,SRAM等<font color='red'>动态内存</font>分配实现
单片机内外部资源操作篇之数码管静态显示
首先,来了解下数码管显示原理: (本图片来源:百度) 数码管是靠点亮内部的发光二极管来显示。一位数码管有10个引脚,显示一个8字需要7个小段,再加上一个小数点,所以内部总共有8个小的发光二极管, 第3个引脚与第8个引脚连接在一起为公共端。如果这个公共端接地,则为共阴极数码管;如果接+5V电压,则为共阳极数码管。如下图所示: 我所用的实验板上的数码管是共阴极数码管。下图为数码管与单片机I/O口连接图: 我们来看原理图: ①.图中U1、U2为两个74HC573锁存器,U1用来向数码管送段选信号,U2用来向数码管送位选信号。U2的输出端Q0~Q5分别接到六个数码管的
[单片机]
单片机内外部资源操作篇之数码管<font color='red'>静态</font>显示
延长电池续航能力,Dialog静态电流PMIC系列 – DA9070和DA9073
高度集成定制和可配置电源管理、AC/DC电源转换、充电和蓝牙低功耗技术供应商 Dialog半导体公司 日前宣布,推出首个针对物联网(IoT)应用的完全集成的纳安级静态电流PMIC系列 – DA9070和DA9073。该最新且更强大的PMIC系列建立在Dialog第一代纳安级静态电流PMIC系列的成功基础之上,进一步彰显了Dialog突破其PMIC技术界限,提升低功耗IoT应用的性能。 今天的‘始终开启’的IoT设备,如健康追踪器、智能手表和智能家居产品,要求尽量少花时间在充电上,但是越来越丰富的功能对设备的电池续航能力提出了新的挑战。由于电路板空间对工程师来说已经非常宝贵,因此新型电源管理解决方案越来越比扩大电池容量更受
[电源管理]
延长电池续航能力,Dialog<font color='red'>静态</font>电流PMIC系列 – DA9070和DA9073
51单片机堆栈操作指令举例说明
堆栈操作 指令 有两条: PUSH direct POP direct 第一条指令称之为推入,就是将direct中的内容送入堆栈中,第二条指令称之为弹出,就是将堆栈中的内容送回到direct中。推入指令的执行过程是,首先将SP中的值加1,然后把SP中的值当作地址,将direct中的值送进以SP中的值为地址的RAM单元中。例: MOV SP,#5FH MOV A,#100 MOV B,#20 PUSH ACC PUSH B 则执行第一条PUSH ACC指令是这样的:将SP中的值加1,即变为60H,然后将A中的值送到60H单元中,因此执行完本条指令后, 内存60H单元的值就是100,同样,执行PUSH B时,是将SP+
[单片机]
单片机模块——数码管(上,静态显示)
数码管其实是LED显示器。分静态和动态显示 静态显示 静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。 我的理解:静态显示只能让所有数码管同时显示一个内容 动态显示 动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。 我的理解:动态显示其实由于人眼分辨率和刷新频率有限,通过一个数码管显示后在他消失前立刻显示下一位,然后循环刷新来达到人眼看过去
[单片机]
单片机模块——数码管(上,<font color='red'>静态</font>显示)
超宽带窄脉冲发射芯片及应用系统电路实现
前言 超宽带(Ultra-wideband,UWB)是近年来备受关注的一种全新的无线通信技术,其利用极大带宽、极低功耗的无线信号来传输高速信息。超宽带技术通常利用极窄的脉冲信号(宽度小于1ns)来进行数据传输,脉冲信号的时间分辨率很高,可用于精确的定位应用,精度可达到厘米量级。 本文利用单片机和自主设计的TH-UWB02超宽带发射芯片实现了一个超宽带窄脉冲发射机电路,能够发送高速率的窄脉冲超宽带脉冲序列,由接收机解调后可以实现高速数据的无线传输,可用于无线数据传输、射频标签等领域。 电路设计 本文介绍的超宽带窄脉冲发射机采用TH-UWB02实现窄脉冲信号的产生和信号调制,工作电压为1.8V时,能够在每个输入数据的上升沿产生
[应用]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved