堆栈与堆(StackvsHeap):有什么区别?一组图片给你讲清楚!

C语言与CPP编程 2023-12-04 08:31

击上方“C语言与CPP编程”,选择“关注/置顶/星标公众号

干货福利,第一时间送达!

最近有小伙伴说没有收到当天的文章推送,这是因为微信改了推送机制,有一部分小伙伴刷不到当天的文章,一些比较实用的知识和信息,错过了就是错过了,建议大家加个星标⭐️,就能第一时间收到推送。

编写有效的代码需要了解堆栈和堆内存,这使其成为学习编程的重要组成部分。不仅如此,新程序员或职场老手都应该完全熟悉堆栈内存和堆内存之间的区别,以便编写有效且优化的代码。

这篇博文将对这两种内存分配技术进行全面的比较。通过本文的结论,我们将对堆栈和堆内存有一个透彻的了解,从而使我们能够在编程工作中有效地使用它们。


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


内存分配

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

  • 全局段(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);
}
}

Java 中的全局和代码段

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);
}
}

Java 中的堆栈内存使用:演示局部变量和函数调用

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

在上面的代码实例中,我们创建了一个名为 的函数add。该函数采用两个参数作为输入整数并返回它们的sum. 在函数内部add,我们创建了一个局部变量调用sum来存储结果。该变量存储在堆栈内存中。

main函数(或 Python 的顶级脚本)中,我们创建另一个局部变量x并为其分配值5。该变量也存储在堆栈内存中。x然后,我们以和作为参数调用 add 函数10。函数调用及其参数和返回地址都放置在堆栈中。一旦add函数返回,堆栈就会被弹出,删除函数调用和关联的数据,我们可以打印结果。

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


堆栈段为空

1共 9 个

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

2共 9 个

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

3共 9 个

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

4共 9 个

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

5共 9 个

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

6共 9 个

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

7共 9 个

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

8共 9 个

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

9共9 个

以下是 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。我们在这里只讨论堆栈和堆段。

栈段和堆段为空

1共 7 个

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

2共 7 个

局部变量值被赋予值 42

3共 7 个

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

4共 7 个

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

5共 7 个

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

6共 7 个

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

7共7 个

以下是 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 中,内存释放主要通过垃圾回收来处理。尽管如此,我们还是应该注意内存使用模式以避免出现问题。

结论

对于任何寻求编写高效且优化的代码的程序员来说,了解堆栈内存和堆内存之间的差异至关重要。

堆栈内存最适合临时存储、局部变量和函数参数。

堆内存非常适合大型数据结构和具有动态生命周期的对象。

我们需要谨慎选择合适的内存分配方法;我们可以创建高效且性能良好的程序。

每种类型的内存都有其自己的一组功能,使用它们来确保我们软件的性能和资源利用率至关重要。

EOF

一个我十分佩服的朋友阿秀开发了一个互联网大厂面试真题记录网站,支持按照行业公司岗位科目考察时间等查看面试真题,有意者欢迎扫码下方二维码适用~

你好,我是飞宇,本硕均于某中流985 CS就读,先后于百度搜索以及字节跳动电商等部门担任Linux C/C++后端研发工程师。

同时,我也是知乎博主@韩飞宇,日常分享C/C++、计算机学习经验、工作体会,欢迎点击此处查看我以前的学习笔记&经验&分享的资源。

我组建了一些社群一起交流,群里有大牛也有小白,如果你有意可以一起进群交流。

欢迎你添加我的微信,我拉你进技术交流群。此外,我也会经常在微信上分享一些计算机学习经验以及工作体验,还有一些内推机会

加个微信,打开另一扇窗

C语言与CPP编程 C语言/C++开发,C语言/C++基础知识,C语言/C++学习路线,C语言/C++进阶,数据结构;算法;python;计算机基础等
评论
  • 根据环洋市场咨询(Global Info Research)项目团队最新调研,预计2030年全球中空长航时无人机产值达到9009百万美元,2024-2030年期间年复合增长率CAGR为8.0%。 环洋市场咨询机构出版了的【全球中空长航时无人机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球中空长航时无人机总体规模,包括产量、产值、消费量、主要生产地区、主要生产商及市场份额,同时分析中空长航时无人机市场主要驱动因素、阻碍因素、市场机遇、挑战、新产品发布等。报告从中空长航时
    GIRtina 2025-01-09 10:35 54浏览
  • 一个真正的质量工程师(QE)必须将一件产品设计的“意图”与系统的可制造性、可服务性以及资源在现实中实现设计和产品的能力结合起来。所以,可以说,这确实是一种工程学科。我们常开玩笑说,质量工程师是工程领域里的「侦探」、「警察」或「律师」,守护神是"墨菲”,信奉的哲学就是「墨菲定律」。(注:墨菲定律是一种启发性原则,常被表述为:任何可能出错的事情最终都会出错。)做质量工程师的,有时会不受欢迎,也会被忽视,甚至可能遭遇主动或被动的阻碍,而一旦出了问题,责任往往就落在质量工程师的头上。虽然质量工程师并不负
    优思学院 2025-01-09 11:48 73浏览
  • HDMI 2.2 规格将至,开启视听新境界2025年1月6日,HDMI Forum, Inc. 宣布即将发布HDMI规范2.2版本。新HDMI规范为规模庞大的 HDMI 生态系统带来更多选择,为创建、分发和体验理想的终端用户效果提供更先进的解决方案。新技术为电视、电影和游戏工作室等内容制作商在当前和未来提供更高质量的选择,同时实现多种分发平台。96Gbps的更高带宽和新一代 HDMI 固定比率速率传输(Fixed Rate Link)技术为各种设备应用提供更优质的音频和视频。终端用户显示器能以最
    百佳泰测试实验室 2025-01-09 17:33 40浏览
  •  在全球能源结构加速向清洁、可再生方向转型的今天,风力发电作为一种绿色能源,已成为各国新能源发展的重要组成部分。然而,风力发电系统在复杂的环境中长时间运行,对系统的安全性、稳定性和抗干扰能力提出了极高要求。光耦(光电耦合器)作为一种电气隔离与信号传输器件,凭借其优秀的隔离保护性能和信号传输能力,已成为风力发电系统中不可或缺的关键组件。 风力发电系统对隔离与控制的需求风力发电系统中,包括发电机、变流器、变压器和控制系统等多个部分,通常工作在高压、大功率的环境中。光耦在这里扮演了
    晶台光耦 2025-01-08 16:03 84浏览
  • 故障现象一辆2017款东风风神AX7车,搭载DFMA14T发动机,累计行驶里程约为13.7万km。该车冷起动后怠速运转正常,热机后怠速运转不稳,组合仪表上的发动机转速表指针上下轻微抖动。 故障诊断 用故障检测仪检测,发动机控制单元中无故障代码存储;读取发动机数据流,发现进气歧管绝对压力波动明显,有时能达到69 kPa,明显偏高,推断可能的原因有:进气系统漏气;进气歧管绝对压力传感器信号失真;发动机机械故障。首先从节气门处打烟雾,没有发现进气管周围有漏气的地方;接着拔下进气管上的两个真空
    虹科Pico汽车示波器 2025-01-08 16:51 107浏览
  • 1月7日-10日,2025年国际消费电子产品展览会(CES 2025)盛大举行,广和通发布Fibocom AI Stack,赋智千行百业端侧应用。Fibocom AI Stack提供集高性能模组、AI工具链、高性能推理引擎、海量模型、支持与服务一体化的端侧AI解决方案,帮助智能设备快速实现AI能力商用。为适应不同端侧场景的应用,AI Stack具备海量端侧AI模型及行业端侧模型,基于不同等级算力的芯片平台或模组,Fibocom AI Stack可将TensorFlow、PyTorch、ONNX、
    物吾悟小通 2025-01-08 18:17 49浏览
  • 「他明明跟我同梯进来,为什么就是升得比我快?」许多人都有这样的疑问:明明就战绩也不比隔壁同事差,升迁之路却比别人苦。其实,之间的差异就在于「领导力」。並非必须当管理者才需要「领导力」,而是散发领导力特质的人,才更容易被晓明。许多领导力和特质,都可以通过努力和学习获得,因此就算不是天生的领导者,也能成为一个具备领导魅力的人,进而被老板看见,向你伸出升迁的橘子枝。领导力是什么?领导力是一种能力或特质,甚至可以说是一种「影响力」。好的领导者通常具备影响和鼓励他人的能力,并导引他们朝着共同的目标和愿景前
    优思学院 2025-01-08 14:54 93浏览
  • 在当前人工智能(AI)与物联网(IoT)的快速发展趋势下,各行各业的数字转型与自动化进程正以惊人的速度持续进行。如今企业在设计与营运技术系统时所面临的挑战不仅是技术本身,更包含硬件设施、第三方软件及配件等复杂的外部因素。然而这些系统往往讲究更精密的设计与高稳定性,哪怕是任何一个小小的问题,都可能对整体业务运作造成严重影响。 POS应用环境与客户需求以本次分享的客户个案为例,该客户是一家全球领先的信息技术服务与数字解决方案提供商,遭遇到一个由他们所开发的POS机(Point of Sal
    百佳泰测试实验室 2025-01-09 17:35 46浏览
  • 在智能网联汽车中,各种通信技术如2G/3G/4G/5G、GNSS(全球导航卫星系统)、V2X(车联网通信)等在行业内被广泛使用。这些技术让汽车能够实现紧急呼叫、在线娱乐、导航等多种功能。EMC测试就是为了确保在复杂电磁环境下,汽车的通信系统仍然可以正常工作,保护驾乘者的安全。参考《QCT-基于LTE-V2X直连通信的车载信息交互系统技术要求及试验方法-1》标准10.5电磁兼容试验方法,下面将会从整车功能层面为大家解读V2X整车电磁兼容试验的过程。测试过程揭秘1. 设备准备为了进行电磁兼容试验,技
    北汇信息 2025-01-09 11:24 60浏览
  • 职场是人生的重要战场,既是谋生之地,也是实现个人价值的平台。然而,有些思维方式却会悄无声息地拖住你的后腿,让你原地踏步甚至退步。今天,我们就来聊聊职场中最忌讳的五种思维方式,看看自己有没有中招。1. 固步自封的思维在职场中,最可怕的事情莫过于自满于现状,拒绝学习和改变。世界在不断变化,行业的趋势、技术的革新都在要求我们与时俱进。如果你总觉得自己的方法最优,或者害怕尝试新事物,那就很容易被淘汰。与其等待机会找上门,不如主动出击,保持学习和探索的心态。加入优思学院,可以帮助你快速提升自己,与行业前沿
    优思学院 2025-01-09 15:48 40浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2025-01-09 09:58 38浏览
  • 在过去十年中,自动驾驶和高级驾驶辅助系统(AD/ADAS)软件与硬件的快速发展对多传感器数据采集的设计需求提出了更高的要求。然而,目前仍缺乏能够高质量集成多传感器数据采集的解决方案。康谋ADTF正是应运而生,它提供了一个广受认可和广泛引用的软件框架,包含模块化的标准化应用程序和工具,旨在为ADAS功能的开发提供一站式体验。一、ADTF的关键之处!无论是奥迪、大众、宝马还是梅赛德斯-奔驰:他们都依赖我们不断发展的ADTF来开发智能驾驶辅助解决方案,直至实现自动驾驶的目标。从新功能的最初构思到批量生
    康谋 2025-01-09 10:04 54浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦