“堆"和"栈"在 C/C++ 中有两个不同层面的含义:数据结构层面和系统内存管理层面。初学者经常混淆,本文从两个角度分别说清楚。


一、数据结构层面

栈(Stack)堆(Heap)
规则后进先出(LIFO)满足堆性质的完全二叉树(最大堆/最小堆)
用途函数调用栈、表达式求值、DFS堆排序、优先队列
实现顺序栈或链式栈数组(通常)

这是数据结构课程中的概念,与内存管理无关。


二、系统内存管理层面

这是 C/C++ 编程中更常见的语境。

程序的内存布局

一个 C/C++ 程序的内存分为以下区域:

高地址
┌─────────────────┐
│  栈区(stack)    │ ← 向低地址增长
│         ↓        │
│                  │
│  (空闲空间)     │
│                  │
│         ↑        │
│  堆区(heap)     │ ← 向高地址增长
├─────────────────┤
│  全局/静态区      │  全局变量、static 变量
├─────────────────┤
│  常量区           │  字符串常量、const 常量
├─────────────────┤
│  代码区           │  函数体的机器指令
└─────────────────┘
低地址

栈(Stack)

由系统自动管理,用于存放函数参数、局部变量、返回地址等。

特点:

  • 分配/释放速度极快(只需移动栈指针)
  • 大小有限(Windows 下默认 1MB,Linux 下默认 8MB)
  • 内存连续
  • 函数结束时自动释放
void foo() {
    int a = 10;        // 栈上分配
    char buf[256];     // 栈上分配
    // 函数返回时,a 和 buf 自动释放
}

堆(Heap)

由程序员手动管理malloc/freenew/delete),用于动态分配内存。

特点:

  • 大小灵活,受限于系统虚拟内存
  • 内存不连续(通过链表管理空闲块)
  • 分配/释放较慢(需要搜索空闲链表)
  • 忘记释放会导致内存泄漏
void bar() {
    int *p = new int[1000];   // 堆上分配
    // ...
    delete[] p;                // 必须手动释放
}

栈 vs 堆 对比

维度
管理系统自动程序员手动
速度快(指针移动)慢(链表查找)
大小小(MB 级)大(受虚拟内存限制)
连续性连续不连续
生命周期函数结束自动释放需手动释放
碎片容易产生

三、实例:各变量存放在哪里

int g = 0;                // 全局区(已初始化)
char *p1;                 // 全局区(未初始化)

int main() {
    int b;                // 栈
    char s[] = "abc";     // 栈(数组在栈上,内容运行时复制)
    char *p2;             // 栈(指针本身在栈上)
    char *p3 = "123456";  // 栈(指针),"123456" 在常量区
    static int c = 0;     // 全局/静态区

    p1 = (char *)malloc(10);  // malloc 分配的 10 字节在堆上
    p2 = (char *)malloc(20);  // malloc 分配的 20 字节在堆上

    strcpy(p1, "123456");     // "123456" 在常量区,复制到堆上
    // 编译器可能将 p1 和 p3 指向的 "123456" 优化为同一份
}

四、存取效率对比

char s1[] = "hello";    // 数组在栈上,内容运行时复制
char *s2 = "world";     // 指针在栈上,指向常量区的字符串

访问 s1[0]:通过栈帧偏移直接读取,一条指令。

访问 s2[0]:先从栈上读取指针值,再通过指针间接访问目标地址,两条指令。

; s1[1] — 直接通过栈帧偏移读取
mov cl, byte ptr [ebp-0Fh]

; s2[1] — 先读指针,再间接寻址
mov edx, dword ptr [ebp-14h]    ; 读取指针
mov al, byte ptr [edx+1]        ; 间接访问

栈上数组的访问效率略高于指针间接访问,因为少了一次内存读取。


五、总结

比喻:

  • 像去餐馆吃饭——点菜、吃、走人,快但选择有限
  • 像自己做饭——买菜、做菜、洗碗,慢但自由度高

一句话: 栈是系统管、快而小;堆是自己管、慢而大。

注意:操作系统层面的"堆"和数据结构中的"堆"是完全不同的概念,只是英文都叫 heap。日常编程中说"堆和栈”,通常指内存管理层面。