fivsevn-devlog

堆(heap)和栈(stack)

那个“栈”经常会在各种文章里看到。  
先不管专业定义,先看一下外行也能稍微理解一下的意思。  

英文原本的意思

stack和heap都是放东西,stack可以看到一层一层的,heap就是一堆团在一起。


先用大厨做饭的例子理解一下

👩‍🍳大厨在家做饭,要用到【厨房台面】和【冰箱/储物柜】。


stack(栈):大厨手边的料理台工作区

特点!
· 用完即走
· 只放当前步骤要用的东西
· 顺序明确,越新的东西越放在“最上层”
· 空间有限,挤满了就不好用了
· 任务结束,台面会自动恢复空白

heap(堆):大厨的冰箱、放干货的储物柜等等。

特点!
· 容量大、可以随便塞
· 用不用无所谓,可以放很久
· 不按顺序,随便放
· 要靠大厨手动整理,否则会越来越乱
· 不会自动清理,除非你开冰箱清空

对照计算机术语


让GPT给了几个具体的使用场景,随便熟悉一下

一、Stack 在真实开发里的典型用法

例句 1:函数里放太多东西,stack 会爆

“别在函数里定义 20MB 的数组,stack 会直接爆掉。”

场景: 某人写了一个函数,里面搞了个巨大的临时数组,程序直接崩。

真实原因:stack 空间固定(比如只有 1MB),装不下。

例句 2:这个变量别放 stack 上,生命周期太短

“你要返回这个对象,不能放 stack,它出了函数就没了。”

场景: 新手把一个结构体声明在函数里,还 return 它的地址。 运行结果:指针指向的内存瞬间消失。

stack 变量 = 函数结束即消失。

例句 3:递归太深了,Stack Overflow(栈溢出)

“这递归 5000 层了,stack 顶不住。”

场景: 深度递归每进入一层,就压一层 stack frame,最后爆掉。

你肯定听过 Stack Overflow(程序错误,不是那个网站),就是字面含义。

例句 4:这个函数调用链太长,stack 占用很大

“这个算法层级太深,stack frame 撑满了,得改循环写法。”

场景: 函数 A 调 B 调 C 调 D…… 每一层都在 stack 上放一帧。

二、Heap 在真实开发里的典型用法

例句 1:别忘了 free,不然 heap 会泄漏(内存泄漏)

“你 malloc 了不 free,这程序跑一小时 heap 都被你泄得差不多了。”

场景: 常见 bug:申请 heap 空间但忘了释放。

heap 内存泄漏就像你冰箱里丢垃圾,不清会越来越多。

例句 2:这个对象很大,放 stack 不行,放 heap

“这个 10MB 的结构必须 new 分配,stack 放不下。”

场景: 大对象必须放 heap,stack 会爆。

例句 3:GC(垃圾回收)在清 heap

“程序卡一下,是在做 heap 的垃圾回收。”

场景: Java / Go / Python 某次停顿: 垃圾回收器正在整理 heap。

你能想象成:程序暂停一下清冰箱。

例句 4:Heap 碎片太严重了,得 compact 一下

“堆碎片太多了,GC compact 一下才能挤出连续空间。”

场景: heap 会碎掉变成不连续的小块,导致申请大对象失败。

stack 永远不会碎片化,heap 会。

例句 5:这个对象生命周期不确定,用 heap 合适

“这个状态要跨好几个函数,用 heap 存吧。”

场景: 程序里长期存在的对象,比如用户会话、游戏角色。

三、Stack 和 Heap 一起出现的真实语境

例句 1:stack 上放指针,heap 才放真实数据

“别搞混了,你在 stack 上放的是指向 heap 的指针,不是对象本身。”

场景: 常见误区: 以为 new 出来的对象在 stack 上,实际上: • stack:存指针 • heap:存对象

例句 2:stack frame 分配很快,heap 分配要走 allocator

“这个函数很快,因为全是 stack 分配,没有 heap 分配。”

场景: 性能优化时常说:stack 分配快如闪电,heap 慢得多。

例句 3:为了性能,减少 heap 分配,多用 stack

“GC 太慢了,把短期对象都放到 stack 上更快。”

场景: 高性能服务器里,程序员会尽量减少 heap 分配。

例句 4:不要从 stack 返回地址,不然指向无效内存

“你返回了一个 stack 地址,出了函数这个东西已经没了。”

场景: C/C++ 新手经典错误。

例句 5:这个对象生命周期不匹配,应该从 stack 改到 heap

“这个对象要跨函数用,别放 stack。”

场景: 设计对象生命周期时的常见讨论。

四、最典型的真实场景故事(完整体验 stack 和 heap)

场景:处理用户请求(一个 Web 服务器)

五、拿【处理 Meshtastic / LoRa 节点上传的数据】举例

python

def handle_message(node_id, raw_payload):
    # ------------- STACK(函数临时工作区)-------------
    # 这些变量存在于函数调用期间,用完就被丢弃
    
    parsed = raw_payload.decode(utf-8)      # 临时解析结果 → stack
    print(f收到节点 {node_id} 的消息{parsed})

    # 创建一个临时 dict(仍在 stack 上,短期变量)
    result = {
        node: node_id,
        text: parsed,
    }

    # 调用另一个函数,把 result 传下去
    store_message(result)  # result 会作为“值”传递,但引用活在 heap(见下)
    # ---------------------------------------------------


def store_message(msg):
    # ------------- STACK(此函数自己的临时空间)-------------
    # msg 是一个引用变量,放在 stack
    # 但 msg 真实内容(dict)在 heap

    print(正在保存消息...)

    # 这个 append 会把 msg(字典对象)放入 heap 里的全局列表
    MESSAGE_DB.append(msg)   # 这里的 MESSAGE_DB 和里面的对象在 heap
    # ---------------------------------------------------


# ------------- HEAP(进程长期存放数据的区域)-------------
# 程序运行期间一直存在,只要你不关程序,这里就不会消失

MESSAGE_DB = []   # 全局列表 → heap,存活时间 = 程序整个生命周期

# ----------------------------------------------------------


# 模拟收到一条 LoRa / Meshtastic 数据
handle_message(node_01, bHello Mesh)
handle_message(node_02, bGPS: 31.2304,121.4737)

print(\n所有存档的消息:“)
for m in MESSAGE_DB:
    print(m)

运行结果:

收到节点 node_01 的消息:Hello Mesh
正在保存消息...
收到节点 node_02 的消息:GPS: 31.2304,121.4737
正在保存消息...

所有存档的消息:
{’node‘: ’node_01‘, ’text‘: ’Hello Mesh‘}
{’node‘: ’node_02‘, ’text‘: ’GPS: 31.2304,121.4737‘}

目前不一定要全部都看懂,但是可以理解一下意思。