当前位置: 首页 > article >正文

数据结构:栈(Stack)和堆(Heap)

目录

内存(Memory)基础

程序是如何利用主存的?

🎯 静态内存分配 vs 动态内存分配 

栈(stack) 

程序执行过程与栈帧变化

 堆(Heap)

程序运行时的主存布局 


内存(Memory)基础

计算机运行程序时,需要在某个地方存储数据和指令,这个地方就是内存,也叫主存或 RAM(随机存取存储器)。

内存被划分为一个个小的单位,每个单位称为字节(byte)。

  • 1字节 = 8位(bit),每个位可以是0或1。

  • 每个字节都有一个唯一的地址,CPU通过这些地址来访问内存中的数据。

  • 内存的地址通常从0开始,依次递增。例如,主存64KB的内存,地址范围是0到65,535(因为64KB = 64 × 1024 = 65,536字节)。

你可以把内存想象成一个超大的格子仓库,每个格子能存放一个数据单位(字节),而程序运行时会不断地从这个仓库取数据、放数据。

程序是如何利用主存的?

当一个程序被加载进内存运行时,主存会被划分成几个不同的区域来完成不同的任务,常见的有三部分:

+-------------------+ ← 低地址(地址编号小)
| 代码区(Text)     | ← 存储编译后的程序指令
+-------------------+
| 栈区(Stack)      | ← 自动变量、函数调用相关的数据
|                   |
|  ↓  向下增长       |
+-------------------+
|                   |
|  ↑  向上增长       |
| 堆区(Heap)       | ← 动态分配的内存(malloc/new)
+-------------------+ ← 高地址(地址编号大)
区域作用
代码区存放程序编译后的“机器指令”,CPU会按顺序执行这些代码。
栈区存储局部变量,如函数里的变量,随着函数调用和退出自动分配和释放。
堆区程序运行时用来“手动”申请的内存区域,由程序员自己管理(使用 newmalloc 等)。

 我们来写一段简单的代码:

#include <iostream>
using namespace std;int main() {int a = 10;        // 栈上的 int 变量float* p = new float(3.14f); // 堆上的 float 变量return 0;
}

这段代码里:

🎯1. int a = 10;

  • a 是一个局部变量,在 main() 函数中声明。

  • 它会被分配在 栈区 中。

  • 如果 int 占 4 字节,比如从地址 0x1000 开始,则 a 占用地址 0x1000 ~ 0x1003

🎯2. new float(3.14f)

  • 这是通过 new 申请的内存,分配在 堆区。

  • 它返回一个指针 p,指向堆上某块内存(比如地址 0x5000),那里存着 3.14

  • p 本身是一个局部变量(指针变量),也放在 栈区,但它指向的内容在 堆区。

🔍 内存示意图 

地址        内容                所属区域
0x1000      10                 a(int 类型,栈上)
0x1004      0x5000             p(指向堆内 float 的地址,栈上)
...
0x5000      3.14               堆上 float

🎯 静态内存分配 vs 动态内存分配 

在 C/C++ 中,int 类型的大小不是写死的,而是取决于以下几个因素:

决定因素说明
平台架构32 位系统中通常是 4 字节(32 位);64 位系统也通常是 4 字节(但不一定)。
编译器的实现不同编译器(如 gcc、MSVC)可能会有不同的默认设置。
数据模型(如 LP64)C/C++ 标准没有规定 int 的精确大小,只规定了大小关系(比如 intshort)。

静态内存分配(Static Memory Allocation)

定义:在编译时确定变量的大小和存储位置,程序运行时自动分配和释放。通常发生在栈区或全局/静态区。

示例:int a = 10;

  • 变量 a 是局部变量,分配在栈上。

  • 编译器知道它的类型(int),所以知道它需要 4 个字节。

  • 编译时就决定:a 需要多大、存哪里、何时释放。

动态内存分配(Dynamic Memory Allocation)

定义:程序运行时,向堆区申请内存。程序员需要自己手动释放。

示例:float* p = new float(3.14f);

  • new float(3.14f) 会在堆上开辟 4 字节(float 大小)的空间。

  • 返回一个地址(比如 0x5000)给 pp 是一个放在栈上的指针变量。

  • 你必须 delete p; 否则会造成内存泄漏。

栈(stack) 

栈是一块在主存中专门用于管理函数调用和局部变量的区域。

它遵循的是一种 “先进后出(FILO)” 的数据结构。

在程序运行时,每次函数调用都会创建一个“栈帧(stack frame)”,这个栈帧保存:

  • 函数的参数(如 int i

  • 函数内部定义的局部变量(如 int afloat b

  • 返回地址(调用完了返回哪)

函数退出后,对应的栈帧就会被销毁,空间自动回收。

 

void fun2(int i)
{int a;
}
void fun1()
{int x;fun2(x);
}
int main()
{int a;float b;fun1();
}

程序执行过程与栈帧变化

假设系统中:

  • intfloat 都是 4 字节

  • 栈从高地址向低地址生长(这是几乎所有平台上的标准)

✅ 步骤 1:程序启动

程序开始执行 main(),栈上创建 main 的栈帧:

main 栈帧:
-----------------------
| float b (4 字节)   |
| int a   (4 字节)   |
-----------------------

✅ 步骤 2:main 调用 fun1()

此时压栈一个新的 fun1 栈帧:

fun1 栈帧:
-----------------------
| int x   (4 字节)   |
-----------------------

✅ 步骤 3:fun1 调用 fun2()

假设 fun2 中传入的参数是某个整数(比如默认是 0),再压一个 fun2 的栈帧:

fun2 栈帧:
-----------------------
| int a   (4 字节)   |
| int i   (4 字节)   | ← 这是参数 i
-----------------------

 此时,整个栈结构如下(高地址在上):

地址       内容(栈帧)     所属函数
0x7ff0     int i           ← fun2 参数
0x7fec     int a           ← fun2 局部变量
-------------------------------------
0x7fe8     int x           ← fun1 局部变量
-------------------------------------
0x7fe4     float b         ← main 局部变量
0x7fe0     int a           ← main 局部变量

函数返回后的栈变化

  • fun2() 执行完毕,它的栈帧会被销毁(ia 消失)

  • 接着 fun1() 返回,它的 x 被销毁

  • 最后 main() 结束,整个栈清空,程序结束

栈的特点总结

特点描述
自动管理函数调用时创建,返回时销毁
生命周期短局部变量只在函数内部有效
空间小但访问快通常 1~8MB,访问速度快于堆
按顺序进出(先进后出)后调用的函数先返回

 堆(Heap)

在程序运行时,堆(heap)是一块用于动态内存分配的区域,由操作系统统一管理。 

  • 与栈不同,堆上的内存不会自动释放,程序员必须手动释放(如 deletefree)。

  • 在 C++ 中,使用 new / new[] 来从堆中申请内存,使用 delete / delete[] 来释放。

 

程序运行时的主存布局 

int main() 
{int* p;p = new int[5];   // 动态申请堆上的数组delete[] p;       // 手动释放堆内存
}
栈区:
--------------------------
| p(指针,8字节)       | ← 指向堆上数组的首地址
--------------------------堆区:
--------------------------
| int[0]                 |
| int[1]                 |
| int[2]                 |
| int[3]                 |
| int[4]                 | ← 共 20 字节
--------------------------

说明:

  • p 是一个局部变量,存储在栈上。

  • new int[5] 在堆上申请了一个连续的数组。

  • p 指向这块堆内内存的起始地址。

💥 如果不 delete[] 会怎样?

如果你忘记 delete[] p;,这块堆内存将不会被释放,就造成了:

  • 内存泄漏(Memory Leak):申请的内存没人管了,程序退出前都无法使用或释放。

在长期运行的程序中,反复申请但不释放,会导致内存耗尽、程序崩溃。

相关文章:

数据结构:栈(Stack)和堆(Heap)

目录 内存&#xff08;Memory&#xff09;基础 程序是如何利用主存的&#xff1f; &#x1f3af; 静态内存分配 vs 动态内存分配 栈&#xff08;stack&#xff09; 程序执行过程与栈帧变化 堆&#xff08;Heap&#xff09; 程序运行时的主存布局 内存&#xff08;Memo…...

用 Vue 做一个轻量离线的“待办清单 + 情绪打卡”小工具

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…...

3D Gaussian splatting 05: 代码阅读-训练整体流程

目录 3D Gaussian splatting 01: 环境搭建3D Gaussian splatting 02: 快速评估3D Gaussian splatting 03: 用户数据训练和结果查看3D Gaussian splatting 04: 代码阅读-提取相机位姿和稀疏点云3D Gaussian splatting 05: 代码阅读-训练整体流程3D Gaussian splatting 06: 代码…...

Linux——计算机网络基础

一、网络 1.概念 由若干结点和连接结点的链路组成。结点可以是计算机&#xff0c;交换机&#xff0c;路由器等。 2.互联网 多个网络连接起来就是互联网。 因特网&#xff1a;最大的互联网。 二、IP地址和MAC地址 1.IP地址 &#xff08;1&#xff09;概念 IP地址是给因…...

第2章_Excel_知识点笔记

来自&#xff1a; 第2章_Excel_知识点笔记 原笔记 Excel 知识点总结&#xff08;第2章&#xff09; Excel_2.1 知识点 基础操作 状态栏&#xff1a;快速查看计数/求和等数据&#xff08;右键可配置&#xff09;。筛选&#xff08;CtrlShiftL&#xff09;&#xff1a;按条件显…...

缩量和放量指的是什么?

在股票市场中&#xff0c;“缩量”和“放量”是描述成交量变化的两个核心概念&#xff0c;它们反映了市场参与者的情绪和资金动向&#xff0c;对判断股价趋势有重要参考价值。以下是具体解析&#xff1a; &#x1f4c9; 一、缩量&#xff08;成交量明显减少&#xff09; 1. 定…...

PostgreSQL数据库备份

文章目录 pg_dump 和 pg_dumpall使用 pg_dump 备份单个数据库示例 使用 pg_dumpall 备份整个数据库集群基本用法 恢复备份恢复 pg_dump 备份恢复 pg_dumpall 备份 Tips pg_dump 和 pg_dumpall 在 PostgreSQL 中&#xff0c;pg_dump 和 pg_dumpall 是两个常用的备份工具&#x…...

企业级Spring MVC高级主题与实用技术讲解

企业级Spring MVC高级主题与实用技术讲解 本手册旨在为具备Spring MVC基础的初学者&#xff0c;系统地讲解企业级应用开发中常用的高级主题和实用技术&#xff0c;涵盖RESTful API、统一异常处理、拦截器、文件处理、国际化、前端集成及Spring Security基础。内容结合JavaConf…...

js-day7

JS学习之旅-day7 1.事件流1.1 事件流与两个阶段说明1.2 事件捕获1.3 事件冒泡1.4 阻止1.5 解绑事件 2. 事件委托3. 其他事件3.1 页面加载事件3.2 页面滚动事件3.3 页面尺寸事件 4. 元素尺寸与位置 1.事件流 1.1 事件流与两个阶段说明 事件流指的是事件完整执行过程中的流动路…...

【算法训练营Day04】链表part2

文章目录 两两交换链表中的节点删除链表的倒数第 N 个结点链表相交环形链表 II链表总结 两两交换链表中的节点 题目链接&#xff1a;24. 两两交换链表中的节点 算法逻辑&#xff1a; 添加一个虚拟头节点初始化一个交换指针&#xff0c;代表每次交换指针的后两个节点&#xff0…...

【ROS2】各种相关概念汇总解释

包含概念 ROS2自带的标准接口ament_cmake是什么&#xff1f; 标准接口 似乎没有一个确定的名称&#xff0c;就是通俗的叫做“ROS2自带的消息接口” 这些接口存放在 /opt/ros/humble/share 路径下 ament_cmake 是 ROS 2 中基于 CMake 的构建系统 系统越复杂&#xff0c;构…...

解决Vditor加载Markdown网页很慢的问题(Vite+JS+Vditor)

1. 引言 在上一篇文章《使用Vditor将Markdown文档渲染成网页(ViteJSVditor)》中&#xff0c;详细介绍了通过Vditor将Markdown格式文档渲染成Web网页的过程&#xff0c;并且实现了图片格式居中以及图片源更换的功能。不过&#xff0c;笔者发现在加载这个渲染Markdown网页的时候…...

Flowise 本地部署文档及 MCP 使用说明

一、Flowise 简介 Flowise 是一个开源的拖放式 UI 工具,用于构建自定义的 LLM 工作流程。它允许用户通过可视化界面连接不同的 AI 组件,无需编写代码即可创建复杂的 AI 应用。 二、Docker 环境安装 1. 构建 Docker 镜像 docker build -t node22-ubuntu-dev .其中Dockerfi…...

YOLO学习笔记 | 一种用于海面目标检测的多尺度YOLO算法

多尺度YOLO算法用于海面目标检测 核心挑战分析 恶劣天气:雨雾、低光照干扰图像质量波浪干扰:动态背景产生大量噪声多尺度目标:船只(大)、浮标(小)等尺度差异大目标遮挡:波浪导致目标部分遮挡算法原理 多尺度YOLO架构(基于YOLOv5改进): graph TD A[输入图像] --&g…...

鸿蒙5.0项目开发——横竖屏切换开发

横竖屏切换开发 【高心星出品】 文章目录 横竖屏切换开发运行效果窗口旋转配置module.json5的orientation字段调用窗口的setPreferredOrientation方法案例代码解析Index1页面代码&#xff1a;EntryAbility在module.json5的配置信息&#xff1a;Index页面的代码信息&#xff1…...

Triton推理服务器部署YOLOv8(onnxruntime后端和TensorRT后端)

文章目录 一、Trition推理服务器基础知识1)推理服务器设计概述2)Trition推理服务器quickstart(1)创建模型仓库(Create a model Repository)(2)启动Triton (launching triton)并验证是否正常运行(3)发送推理请求(send a inference request)3)Trition推理服务器架…...

TDengine 的 AI 应用实战——电力需求预测

作者&#xff1a; derekchen Demo数据集准备 我们使用公开的UTSD数据集里面的电力需求数据&#xff0c;作为预测算法的数据来源&#xff0c;基于历史数据预测未来若干小时的电力需求。数据集的采集频次为30分钟&#xff0c;单位与时间戳未提供。为了方便演示&#xff0c;按…...

NLP学习路线图(二十一): 词向量可视化与分析

在自然语言处理&#xff08;NLP&#xff09;的世界里&#xff0c;词向量&#xff08;Word Embeddings&#xff09;犹如一场静默的革命。它将原本离散、难以捉摸的词语&#xff0c;转化为稠密、富含语义的连续向量&#xff0c;为机器理解语言铺平了道路。然而&#xff0c;这些向…...

【分布式技术】KeepAlived高可用架构科普

KeepAlived高可用架构 Keepalived 架构详解一、核心架构组件二、VRRP 协议详解1. **VRRP 核心概念**2. **VRRP 工作流程**3. **VRRP 通信机制** 三、高可用架构模型四、健康检查机制五、配置文件详解配置文件关键参数说明&#xff1a; 六、高可用实现流程七、脑裂问题与解决方案…...

如何配置mvn镜像源为华为云

如何配置mvn镜像源为华为云 # 查找mvn 配置文件 mvn -X help:effective-settings | grep settings.xml# 配置mvn镜像源为华为云&#xff0c;/home/apache-maven-3.9.5/conf/settings.xml文件路径需要根据上一步中查询结果调整 cat > /home/apache-maven-3.9.5/conf/setting…...

Linux平台排查CPU占用高的进程和线程指南

基础排查工具 1. top命令 - 实时进程监控 top操作指令&#xff1a; 按 P&#xff1a;按CPU使用率排序按 1&#xff1a;显示每个CPU核心的使用情况按 H&#xff1a;切换显示线程视图按 M&#xff1a;按内存使用排序按 q&#xff1a;退出 2. htop命令 - 增强版top&#xff08…...

多模态大语言模型arxiv论文略读(105)

UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ➡️ 论文标题&#xff1a;UnifiedMLLM: Enabling Unified Representation for Multi-modal Multi-tasks With Large Language Model ➡️ 论文作者&#xff1a;Zhaowei…...

简述MySQL 超大分页怎么处理 ?

针对MySQL超大分页&#xff08;深度分页&#xff09;的性能问题&#xff0c;核心优化方案如下&#xff1a; 1. ‌子查询 覆盖索引&#xff08;延迟关联&#xff09;‌ ‌原理‌&#xff1a; 子查询仅扫描‌覆盖索引‌&#xff08;如主键&#xff09;&#xff0c;避免回表操作…...

Pyhton中的命名空间包(Namespace Package)您了解吗?

在 Python 中&#xff0c;命名空间包&#xff08;Namespace Package&#xff09; 是一种特殊的包结构&#xff0c;它允许将模块分散在多个独立的目录中&#xff0c;但这些目录在逻辑上属于同一个包命名空间。命名空间包的核心特点是&#xff1a;没有 __init__.py 文件&#xff…...

Java设计模式之备忘录模式详解

Java设计模式之备忘录模式详解 一、备忘录模式核心思想 核心目标&#xff1a;捕获对象内部状态并在需要时恢复&#xff0c;同时不破坏对象的封装性。如同游戏存档系统&#xff0c;允许玩家保存当前进度并在需要时回退到之前的状态。 二、备忘录模式类图&#xff08;Mermaid&am…...

Azure DevOps Server 2022.2 补丁(Patch 5)

微软Azure DevOps Server的产品组在4月8日发布了2022.2 的第5个补丁。下载路径为&#xff1a;https://aka.ms/devops2022.2patch5 这个补丁的主要功能是修改了代理(Agent)二进制安装文件的下载路径&#xff1b;之前&#xff0c;微软使用这个CND(域名为vstsagentpackage.azuree…...

手摸手还原vue3中reactive的get陷阱以及receiver的作用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、实例是什么&#xff1f;二、new Prxoy三、实现代码1.引入代码2.读入数据 总结 前言 receiver不是为解决get陷阱而生&#xff0c;而是为解决Proxy中的this绑…...

小明的Java面试奇遇之互联网保险系统架构与性能优化

一、文章标题 小明的Java面试奇遇之互联网保险系统架构与性能优化&#x1f680; 二、文章标签 Java,Spring Boot,MyBatis,Redis,Kafka,JVM,多线程,互联网保险,系统架构,性能优化 三、文章概述 本文模拟了程序员小明在应聘互联网保险系统开发岗位时&#xff0c;参与的一场深…...

C++学习-入门到精通【13】标准库的容器和迭代器

C学习-入门到精通【13】标准库的容器和迭代器 目录 C学习-入门到精通【13】标准库的容器和迭代器一、标准模板库简介1.容器简介2.STL容器总览3.近容器4.STL容器的通用函数5.首类容器的通用typedef6.对容器元素的要求 二、迭代器简介1.使用istream_iterator输入&#xff0c;使用…...

C# 面向对象特性

面向对象编程的三大基本特性是&#xff1a;封装、继承和多态。下面将详细介绍这三大特性在C#中的体现方式。 封装 定义&#xff1a;把对象的数据和操作代码组合在同一个结构中&#xff0c;这就是对象的封装性。 体现方式&#xff1a; 使用访问修饰符控制成员的可见性 通过属…...