图文并茂拆解代码解析堆栈与堆的区别

一起学嵌入式 2023-12-20 09:06

扫描关注一起学嵌入式,一起学习,一起成长


编写有效的代码需要了解堆栈和堆内存,这使其成为学习编程的重要组成部分。不仅如此,新程序员或职场老手都应该完全熟悉堆栈内存和堆内存之间的区别,以便编写有效且优化的代码。
这篇博文将对这两种内存分配技术进行全面的比较。通过本文的结论,我们将对堆栈和堆内存有一个透彻的了解,从而使我们能够在编程工作中有效地使用它们。

对比理解堆栈与堆的结构!

内存分配

内存是计算机编程的基础。它提供了存储数据和程序高效运行所需的所有命令的空间。分配内存可以与在计算机内存中为特定目的指定特定区域进行比较,例如容纳对程序功能至关重要的变量或对象。
程序的内存布局和组织可能会根据所使用的操作系统和体系结构而有所不同。然而,一般来说,内存可以分为以下几个部分:
  • 全局段(Global segment
  • 代码段(Code segment
  • 堆栈(Stack
  • 堆(Heap

全局段,负责存储全局变量和静态变量,这些变量的生命周期等于程序执行的整个持续时间。

代码段,也称为文本段,包含组成我们程序的实际机器代码或指令,包括函数和方法。

堆栈段,用于管理局部变量、函数参数和控制信息(例如返回地址)。

堆段,提供了一个灵活的区域来存储大型数据结构和具有动态生命周期的对象。堆内存可以在程序执行期间分配或释放。

注意:值得注意的是,内存分配上下文中的堆栈和堆不应与数据结构堆栈和堆混淆,它们具有不同的用途和功能。

四个内存段(全局、代码、堆栈和堆)的概述,说明了堆向下增长和堆栈向上增长的常规表示
每个程序都有自己的虚拟内存布局,由操作系统映射到物理内存。每个细分市场的具体分配取决于多种因素,例如:
  • 程序代码的大小。

  • 全局变量的数量和大小。

  • 程序所需的动态内存分配量。

  • 程序使用的调用堆栈的大小。
在任何函数外部声明的全局变量都将驻留在全局段中。程序功能和方法的机器代码或指令将存储在代码段中。让我们看一下编码示例,以帮助可视化全局和代码段在内存中的使用方式:
public class Main {
// Global Segment:全局变量存放在这里
static int globalVar = 42;

// 代码段:这里存放函数和方法
public static int add(int a, int b) {
return a + b;
}

public static void main(String[] args) {
// 代码段:调用add函数
int sum = add(globalVar, 10);

System.out.println("Sum: " + sum);
}
}

globalVar在这些代码示例中,我们有一个值为 的全局变量42,它存储在全局段中。我们还有一个函数add,它接受两个整数参数并返回它们sum该函数存储在代码段中。main函数(或 Python 中的脚本)调用该add函数,传递全局变量和另一个整数值10作为参数。

代码中的全局和代码段(未显示堆和堆栈段)

需要强调的是,管理堆栈和堆段对于代码的性能和效率起着重要作用,使其成为编程的一个重要方面。因此,程序员在深入研究它们的差异之前应该充分理解它们。

栈内存:有序存储

将堆栈内存视为有组织且高效的存储单元。它使用后进先出 (LIFO) 方法,这意味着最近添加的数据将首先被删除。
内核是操作系统的核心组件,自动管理堆栈内存;我们不必担心分配和释放内存。当我们的程序运行时,它会自行处理。

下面不同编程语言的代码实例演示了堆栈在各种情况下的使用。

public class StackExample {

// 一个简单的函数来添加两个数字
public static int add(int a, int b) {
// 局部变量(存储在栈中)
int sum = a + b;
return sum;
}

public static void main(String[] args) {
// 局部变量(存储在栈中)
int x = 5;

// 函数调用(存储在堆栈中)
int result = add(x, 10);

System.out.println("Result: " + result);
}
}

调用函数时会创建称为堆栈帧的内存块。堆栈存储与局部变量、参数和函数的返回地址相关的信息。该内存是在堆栈段上创建的。

在上面的代码实例中,我们创建了一个名为 的函数add。该函数采用两个参数作为输入整数并返回它们的sum. 在函数内部add,我们创建了一个局部变量调用sum来存储结果。该变量存储在堆栈内存中。
main函数(或 Python 的顶级脚本)中,我们创建另一个局部变量x并为其分配值5。该变量也存储在堆栈内存中。x然后,我们以和作为参数调用 add 函数10。函数调用及其参数和返回地址都放置在堆栈中。一旦add函数返回,堆栈就会被弹出,删除函数调用和关联的数据,我们可以打印结果。
在下面的解释中,我们将介绍运行每行重要代码后堆和堆栈如何变化。

堆栈段为空

为主函数创建一个新的堆栈帧

在 main 函数的堆栈帧中,局部变量 x 现在的值为 5

调用 add 函数,实际参数为 (5, 10)

控制权转移到 add 函数,为 add 函数创建一个新的堆栈帧,其中包含局部变量 a、b 和 sum

add 函数的堆栈帧上的 sum 变量被分配 a + b 的结果

add 函数完成其任务并且其堆栈帧被销毁

具有可变结果的主函数的堆栈帧存储从 add 函数返回的值

在显示结果值(此处未显示)后,主功能块也被销毁,并且堆栈段再次为空

以下是 C++ 代码按执行顺序的解释:

  • 第 10 行:程序从该main函数开始,并为其创建一个新的堆栈帧。
  • 第 12 行:局部变量x被赋值为5
  • 第 15 行:add使用参数x和调用该函数10
  • 第 4 行:为该函数创建一个新的堆栈帧add。控制权转移到add带有局部变量的函数。ab、 和sum。变量a和分别被赋予和b的值。x10
  • 第 6 行:局部变量sum被赋值为a + b(即 5 + 10)。
  • 第 7 行:变量sum的值(即 15)被返回给调用者。
  • 第 8 行:add从堆栈中弹出函数的堆栈帧,并释放所有局部变量(、和a) bsum
  • 第15行:result函数堆栈帧上的局部变量main被赋予返回值(即15)。
  • 第 17 行:存储在变量中的值result(即 15)使用 打印到控制台std::cout
  • 第 19 行:函数main返回 0,表示执行成功。
  • 第 20 行:函数main的堆栈帧从堆栈中弹出,并且所有局部变量 (xresult) 都被释放。

堆栈存储器的主要特点

以下是有关堆栈内存需要考虑的一些关键方面:
  • 固定大小:当涉及到堆栈内存时,其大小保持固定,并在程序执行开始时确定。
  • 速度优势:堆栈内存帧是连续的。因此,在堆栈内存中分配和释放内存的速度非常快。这是通过操作系统管理的堆栈指针对引用进行简单调整来完成的。
  • 控制信息和变量的存储:堆栈内存负责容纳控制信息、局部变量和函数参数,包括返回地址。
  • 有限的可访问性:请务必记住,存储在堆栈内存中的数据只能在活动函数调用期间访问。
  • 自动管理:堆栈内存的高效管理由系统本身完成,不需要我们额外的工作。

堆内存:动态存储

堆内存,也称为动态内存,是内存分配的野孩子。程序员必须手动管理它。堆内存允许我们在程序执行期间随时分配和释放内存。它非常适合存储大型数据结构或大小事先未知的对象。

下面不同编程语言的代码实例演示了堆的使用。

public class HeapExample {
public static void main(String[] args) {
// 栈:局部变量“value”存储在 栈中
int value = 42;

// 堆:为堆上的单个 Integer 分配内存
Integer ptr = new Integer(value);

// 将值分配给分配的内存并打印它
System.out.println("Value: " + ptr);

// 在Java中,垃圾收集是自动的,因此不需要 释放内存
}
}

演示 Java 中的堆内存分配和使用

在这些代码示例中,目标是将值存储42在堆内存中,这是一个更永久、更灵活的存储空间。这是通过使用驻留在堆栈内存中的指针或引用变量来完成的:
  • int* ptr在C++中。
  • Java 中的一个Integer对象ptr
  • ptrPython 中包含单个元素的列表。
然后打印存储在堆上的值。在C++中,需要使用delete关键字手动释放堆上分配的内存。然而,Python 和 Java 通过垃圾收集自动管理内存释放,无需手动干预。

注意:在 Java 和 Python 中,垃圾收集会自动处理内存释放,无需手动释放内存,如 C++ 中所示。

在下面的解释中,我们将讨论运行每行重要代码后堆和堆栈如何变化。尽管我们关注的是 C++,但该解释也适用于 Python 和 Java。我们在这里只讨论堆栈和堆段。

栈段和堆段为空

为主函数创建一个新的堆栈帧

局部变量值被赋予值 42

在堆上分配了一个指针变量ptr,指针ptr中存放的是分配的堆内存的地址(即0x1000)

value变量中存储的值(即42)被赋值给ptr指向的内存位置(堆地址0x1000)

堆上地址 0x1000 处分配的内存被释放

main函数的栈帧从栈中弹出(显示result的值后),栈段和堆段再次清空

以下是 C++ 代码按执行顺序的解释:

  • 第 3 行:main调用该函数,并为其创建一个新的堆栈帧。
  • 第 5 行:堆栈帧上的局部变量value被赋值为42
  • 第 8 行:ptr使用关键字为堆上的单个整数动态创建的内存分配给指针变量new。我们假设堆上新内存的地址为 0x1000。分配的堆内存的地址(0x1000)存储在指针中。ptr
  • 第 11 行:将整数值42分配给ptr(堆地址 0x1000)所指向的内存位置。
  • 第 12 行:(ptr )指向的内存位置存储的值42被打印到控制台。
  • 第 15 行:使用关键字释放在堆上地址 0x1000 处分配的内存delete。在此行之后,ptr成为悬空指针,因为它仍然保存地址 0x1000,但该内存已被释放。然而,对于这个重要的讨论,我们不会详细讨论悬空指针。
  • 第17行: main函数返回0,表示执行成功。
  • 第 18 行:从堆栈中弹出主函数的堆栈帧,并释放所有局部变量 (value和)。ptr

注意:C++ 标准库还提供了一系列智能指针,可以帮助自动化堆中内存分配和释放的过程。

堆内存的主要特点

以下是需要记住的堆内存的一些显着特征:

  • 大小的灵活性:堆内存大小可以在程序执行过程中发生变化。
  • 速度权衡:在堆中分配和释放内存速度较慢,因为它涉及寻找合适的内存帧和处理碎片。
  • 动态对象的存储:堆内存存储具有动态生命周期的对象和数据结构,如newJava 或 C++ 中使用关键字创建的对象和数据结构。
  • 持久数据:存储在堆内存中的数据将一直保留在那里,直到我们手动释放它或程序结束。
  • 手动管理:在某些编程语言(例如C和C++)中,必须手动管理堆内存。如果处理不当,可能会导致内存泄漏或资源使用效率低下。

堆栈与堆:差异对比

现在我们彻底了解了堆栈和堆内存分配的工作原理,我们可以区分它们了。在比较栈内存和堆内存时,我们必须考虑它们的独特特性来理解它们的差异:

  • 大小管理:堆栈内存具有在程序执行开始时确定的固定大小,而堆内存是灵活的,可以在程序的整个生命周期中更改。
  • 速度:堆栈内存在分配和释放内存时具有速度优势,因为它只需要调整引用。相反,由于需要定位合适的内存帧并管理碎片,堆内存操作速度较慢。
  • 存储目的:堆栈内存指定用于控制信息(例如函数调用和返回地址)、局部变量和函数参数(包括返回地址)。另一方面,堆内存用于存储具有动态生命周期的对象和数据结构,例如newJava 或 C++ 中使用关键字创建的对象和数据结构。
  • 数据可访问性:堆栈内存中的数据只能在活动函数调用期间访问,而堆内存中的数据在手动释放或程序结束之前仍然可以访问。
  • 内存管理:系统自动管理堆栈内存,优化其使用,以实现快速高效的内存引用。相比之下,堆内存管理是程序员的责任,处理不当可能会导致内存泄漏或资源使用效率低下。

下表总结了堆栈内存和堆内存在不同方面的主要区别:

方面对比

堆栈内存

堆内存

尺寸管理

固定大小,在程序开始时确定

灵活的大小,可以在程序的生命周期中改变

速度

更快,只需要调整一个参考

速度较慢,涉及定位合适的块和管理碎片

储存目的

控制信息、局部变量、函数参数

具有动态生命周期的对象和数据结构

数据可访问性

仅在活动函数调用期间可访问

在手动释放或程序结束之前均可访问

内存管理

由系统自动管理

由程序员手动管理

堆栈内存与堆内存:何时使用每种类型

我们现在知道堆栈内存和堆内存之间的区别。现在让我们看看何时使用每种类型的内存。
堆栈是 C++、Java 和 Python 中存储局部变量和函数参数的默认选项,其生命周期较短且可预测。但在以下情况下建议使用堆内存:
  • 当需要存储对象、数据结构或动态分配的数组时,其生命周期在编译时或函数调用期间无法预测。

  • 当内存需求很大或者我们需要在程序的不同部分之间共享数据时。

  • 当需要分配超出单个函数调用范围的内存时。
此外,C++ 中需要手动内存管理(使用delete),而在 Java 和 Python 中,内存释放主要通过垃圾回收来处理。尽管如此,我们还是应该注意内存使用模式以避免出现问题。

结论

对于任何寻求编写高效且优化的代码的程序员来说,了解堆栈内存和堆内存之间的差异至关重要。
堆栈内存最适合临时存储、局部变量和函数参数。
堆内存非常适合大型数据结构和具有动态生命周期的对象。
我们需要谨慎选择合适的内存分配方法;我们可以创建高效且性能良好的程序。
每种类型的内存都有其自己的一组功能,使用它们来确保我们软件的性能和资源利用率至关重要。

文章来源于网络,版权归原作者所有,如有侵权,请联系删除。



关注【一起学嵌入式】,回复加群进技术交流群。




觉得文章不错,点击“分享”、“”、“在看” 呗!

一起学嵌入式 公众号【一起学嵌入式】,RTOS、Linux编程、C/C++,以及经验分享、行业资讯、物联网等技术知
评论
  • 在物联网领域中,无线射频技术作为设备间通信的核心手段,已深度渗透工业自动化、智慧城市及智能家居等多元场景。然而,随着物联网设备接入规模的不断扩大,如何降低运维成本,提升通信数据的传输速度和响应时间,实现更广泛、更稳定的覆盖已成为当前亟待解决的系统性难题。SoC无线收发模块-RFM25A12在此背景下,华普微创新推出了一款高性能、远距离与高性价比的Sub-GHz无线SoC收发模块RFM25A12,旨在提升射频性能以满足行业中日益增长与复杂的设备互联需求。值得一提的是,RFM25A12还支持Wi-S
    华普微HOPERF 2025-02-28 09:06 143浏览
  • 2025年2月26日,广州】全球领先的AIoT服务商机智云正式发布“Gokit5 AI智能体开发板”,该产品作为行业首个全栈式AIoT开发中枢,深度融合火山引擎云原生架构、豆包多模态大模型、扣子智能体平台和机智云Aiot开发平台,首次实现智能体开发全流程工业化生产模式。通过「扣子+机智云」双引擎协同架构与API开放生态,开发者仅需半天即可完成智能体开发、测试、发布到硬件应用的全流程,标志着智能体开发进入分钟级响应时代。一、开发框架零代码部署,构建高效开发生态Gokit5 AI智能体开发板采用 “
    机智云物联网 2025-02-26 19:01 162浏览
  •         近日,广电计量在聚焦离子束(FIB)领域编写的专业著作《聚焦离子束:失效分析》正式出版,填补了国内聚焦离子束领域实践性专业书籍的空白,为该领域的技术发展与知识传播提供了重要助力。         随着芯片技术不断发展,芯片的集成度越来越高,结构也日益复杂。这使得传统的失效分析方法面临巨大挑战。FIB技术的出现,为芯片失效分析带来了新的解决方案。它能够在纳米尺度上对芯片进行精确加工和分析。当芯
    广电计量 2025-02-28 09:15 116浏览
  • 构建巨量的驾驶场景时,测试ADAS和AD系统面临着巨大挑战,如传统的实验设计(Design of Experiments, DoE)方法难以有效覆盖识别驾驶边缘场景案例,但这些边缘案例恰恰是进一步提升自动驾驶系统性能的关键。一、传统解决方案:静态DoE标准的DoE方案旨在系统性地探索场景的参数空间,从而确保能够实现完全的测试覆盖范围。但在边缘案例,比如暴露在潜在安全风险的场景或是ADAS系统性能极限场景时,DoE方案通常会失效,让我们看一些常见的DoE方案:1、网格搜索法(Grid)实现原理:将
    康谋 2025-02-27 10:00 252浏览
  • 应用趋势与客户需求,AI PC的未来展望随着人工智能(AI)技术的日益成熟,AI PC(人工智能个人电脑)逐渐成为消费者和企业工作中的重要工具。这类产品集成了最新的AI处理器,如NPU、CPU和GPU,并具备许多智能化功能,为用户带来更高效且直观的操作体验。AI PC的目标是提升工作和日常生活的效率,通过深度学习与自然语言处理等技术,实现更流畅的多任务处理、实时翻译、语音助手、图像生成等功能,满足现代用户对生产力和娱乐的双重需求。随着各行各业对数字转型需求的增长,AI PC也开始在各个领域中显示
    百佳泰测试实验室 2025-02-27 14:08 255浏览
  • Matter 协议,原名 CHIP(Connected Home over IP),是由苹果、谷歌、亚马逊和三星等科技巨头联合ZigBee联盟(现连接标准联盟CSA)共同推出的一套基于IP协议的智能家居连接标准,旨在打破智能家居设备之间的 “语言障碍”,实现真正的互联互通。然而,目标与现实之间总有落差,前期阶段的Matter 协议由于设备支持类型有限、设备生态协同滞后以及设备通信协议割裂等原因,并未能彻底消除智能家居中的“设备孤岛”现象,但随着2025年的到来,这些现象都将得到完美的解决。近期,
    华普微HOPERF 2025-02-27 10:32 214浏览
  • RGB灯光无法同步?细致的动态光效设定反而成为产品客诉来源!随着科技的进步和消费者需求变化,电脑接口设备单一功能性已无法满足市场需求,因此在产品上增加「动态光效」的形式便应运而生,藉此吸引消费者目光。这种RGB灯光效果,不仅能增强电脑周边产品的视觉吸引力,还能为用户提供个性化的体验,展现独特自我风格。如今,笔记本电脑、键盘、鼠标、鼠标垫、耳机、显示器等多种电脑接口设备多数已配备动态光效。这些设备的灯光效果会随着音乐节奏、游戏情节或使用者的设置而变化。想象一个画面,当一名游戏玩家,按下电源开关,整
    百佳泰测试实验室 2025-02-27 14:15 137浏览
  • 振动样品磁强计是一种用于测量材料磁性的精密仪器,广泛应用于科研、工业检测等领域。然而,其测量准确度会受到多种因素的影响,下面我们将逐一分析这些因素。一、温度因素温度是影响振动样品磁强计测量准确度的重要因素之一。随着温度的变化,材料的磁性也会发生变化,从而影响测量结果的准确性。因此,在进行磁性测量时,应确保恒温环境,以减少温度波动对测量结果的影响。二、样品制备样品的制备过程同样会影响振动样品磁强计的测量准确度。样品的形状、尺寸和表面处理等因素都会对测量结果产生影响。为了确保测量准确度,应严格按照规
    锦正茂科技 2025-02-28 14:05 134浏览
  • 更多生命体征指标风靡的背后都只有一个原因:更多人将健康排在人生第一顺位!“AGEs,也就是晚期糖基化终末产物,英文名Advanced Glycation End-products,是存在于我们体内的一种代谢产物” 艾迈斯欧司朗亚太区健康监测高级市场经理王亚琴说道,“相信业内的朋友都会有关注,最近该指标的热度很高,它可以用来评估人的生活方式是否健康。”据悉,AGEs是可穿戴健康监测领域的一个“萌新”指标,近来备受关注。如果站在学术角度来理解它,那么AGEs是在非酶促条件下,蛋白质、氨基酸
    艾迈斯欧司朗 2025-02-27 14:50 400浏览
  • 请移步 gitee 仓库 https://gitee.com/Newcapec_cn/LiteOS-M_V5.0.2-Release_STM32F103_CubeMX/blob/main/Docs/%E5%9F%BA%E4%BA%8ESTM32F103RCT6%E7%A7%BB%E6%A4%8DLiteOS-M-V5.0.2-Release.md基于STM32F103RCT6移植LiteOS-M-V5.0.2-Release下载源码kernel_liteos_m: OpenHarmon
    逮到一只程序猿 2025-02-27 08:56 195浏览
  • 美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?美国加州CEC能效跟DOE能效有什么区别?CEC/DOE是什么关系?‌美国加州CEC能效认证与美国DOE能效认证在多个方面存在显著差异‌。认证范围和适用地区‌CEC能效认证‌:仅适用于在加利福尼亚州销售的电器产品。CEC认证的范围包括制冷设备、房间空调、中央空调、便携式空调、加热器、热水器、游泳池加热器、卫浴配件、光源、应急灯具、交通信号模块、灯具、洗碗机、洗衣机、干衣机、烹饪器具、电机和压缩机、变压器、外置电源、消费类电子设备
    张工nx808593 2025-02-27 18:04 120浏览
  •           近日受某专业机构邀请,参加了官方举办的《广东省科技创新条例》宣讲会。在与会之前,作为一名技术工作者一直认为技术的法例都是保密和侵权方面的,而潜意识中感觉法律有束缚创新工作的进行可能。通过一个上午学习新法,对广东省的科技创新有了新的认识。广东是改革的前沿阵地,是科技创新的沃土,企业是创新的主要个体。《广东省科技创新条例》是广东省为促进科技创新、推动高质量发展而制定的地方性法规,主要内容包括: 总则:明确立法目
    广州铁金刚 2025-02-28 10:14 103浏览
  • 一、VSM的基本原理震动样品磁强计(Vibrating Sample Magnetometer,简称VSM)是一种灵敏且高效的磁性测量仪器。其基本工作原理是利用震动样品在探测线圈中引起的变化磁场来产生感应电压,这个感应电压与样品的磁矩成正比。因此,通过测量这个感应电压,我们就能够精确地确定样品的磁矩。在VSM中,被测量的样品通常被固定在一个震动头上,并以一定的频率和振幅震动。这种震动在探测线圈中引起了变化的磁通量,从而产生了一个交流电信号。这个信号的幅度和样品的磁矩有着直接的关系。因此,通过仔细
    锦正茂科技 2025-02-28 13:30 100浏览
  • 1,微软下载免费Visual Studio Code2,安装C/C++插件,如果无法直接点击下载, 可以选择手动install from VSIX:ms-vscode.cpptools-1.23.6@win32-x64.vsix3,安装C/C++编译器MniGW (MinGW在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序.)4,C/C++插件扩展设置中添加Include Path 5,
    黎查 2025-02-28 14:39 140浏览
  • 在2024年的科技征程中,具身智能的发展已成为全球关注的焦点。从实验室到现实应用,这一领域正以前所未有的速度推进,改写着人类与机器的互动边界。这一年,我们见证了具身智能技术的突破与变革,它不仅落地各行各业,带来新的机遇,更在深刻影响着我们的生活方式和思维方式。随着相关技术的飞速发展,具身智能不再仅仅是一个技术概念,更像是一把神奇的钥匙。身后的众多行业,无论愿意与否,都像是被卷入一场伟大变革浪潮中的船只,注定要被这股汹涌的力量重塑航向。01为什么是具身智能?为什么在中国?最近,中国具身智能行业的进
    艾迈斯欧司朗 2025-02-28 15:45 221浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦