从内存栈和堆的角度,给这段 C相加函数代码做个 "内存 CT"

从内存栈和堆的角度,给这段 C相加函数代码做个 "内存 CT"

#include <stdio.h>
 
// 函数外定义变量 x 和 y
int x;
int y;
int addtwonum()
{
    // 函数内声明变量 x 和 y 为外部变量
    extern int x;
    extern int y;
    // 给外部变量(全局变量)x 和 y 赋值
    x = 1;
    y = 2;
    return x+y;
}
 
int main()
{
    int result;
    // 调用函数 addtwonum
    result = addtwonum();
    
    printf("result 为: %d",result);
    return 0;
}

咱们先把这段代码比作一个小公司的办公场景:全局变量像公司的公共打印机,局部变量像员工自己的笔记本,而函数调用就像员工去会议室开会 —— 这样理解内存分配就简单多了。

先给代码做个 "体检报告"

这段段代码做的事儿特简单:定义了全局变量x和y,在函数里给它们赋值后相加,最后输出结果。运行结果肯定是3,但咱们今天不关心结果,专注看内存里发生了啥。

内存里的 "两大区域":栈(Stack)和堆(Heap)

C 语言的内存主要分这两大块,用途完全不同:



  • 栈(Stack):像公司的 "临时会议室",自动分配自动释放,存放局部变量和函数调用信息,特点是 "先进后出"(像叠盘子)。
  • 堆(Heap):像公司的 "长期仓库",需要手动手动申请(malloc)和释放(free),存放动态分配的变量,谁用谁管理。



而咱们今天的主角还有一个特殊区域 ——全局变量区(属于静态存储区,既不是栈也不是堆),像公司的 "公共设备",从程序启动到结束一直存在,所有人都能访问。

逐行分析:内存里的 "演员走位"

  1. 全局变量x和y的内存分配
  2. c
  3. 运行
  4. int x; int y;

  5. 这俩变量定义在函数外面,属于全局变量,存放在全局变量区(静态存储区)。
  6. 程序一启动就会给它们分配内存,默认初始化为0(就像打印机买回来就插好电待命)。
  7. 整个程序运行期间,它们的内存地址不变,所有函数都能访问(就像全公司都能用打印机)。
  8. main函数开始执行
  9. c
  10. 运行
  11. int main() { int result; // 局部变量 // ... }

  12. main函数被调用时,操作系统会在上给它分配一块 "栈帧"(相当于给main函数开了个会议室)。
  13. 局部变量result就存放在main的栈帧里(像参会人员带的笔记本),只在main函数内部有效。
  14. 调用addtwonum函数
  15. c
  16. 运行
  17. result = addtwonum();

  18. 调用函数时,会在上给addtwonum新分配一个栈帧(又开了个小会议室),这个栈帧在main的栈帧 "上面"(叠盘子一样)。
  19. 函数里的extern int x; extern int y;是声明(不是定义),告诉编译器:"xy是外面的全局变量,我要用它们 "(相当于说" 我要去用公司的打印机 ")。
  20. addtwonum函数内部操作
  21. c
  22. 运行
  23. x = 1; y = 2; return x + y;

  24. 这里修改的xy,还是全局变量区的那两个(不是栈上的),相当于给公共打印机换了墨盒。
  25. 计算结果3会暂时存放在栈上(函数返回值的临时存储区)。
  26. 函数返回,栈帧释放addtwonum执行完,它的栈帧会被自动销毁(小会议室解散),但全局变量xy还在(打印机继续待命)。返回值3被赋给main函数里的result(笔记本记上结果)。
  27. main函数结束main的栈帧被销毁(大会议室解散),局部变量result消失。程序结束,全局变量区的xy才会被释放(公司下班,打印机断电)。

画个内存分布图,一目了然

plaintext

┌─────────────────────────────────┐
│  全局变量区                     │
│  ┌─────────┐  ┌─────────┐       │
│  │ x: 1    │  │ y: 2    │       │  ← 整个程序运行期间都在
│  └─────────┘  └─────────┘       │
├─────────────────────────────────┤
│  栈 (Stack)                     │
│  ┌─────────────────────────┐    │
│  │ main函数栈帧            │    │
│  │  ┌─────────────────┐    │    │
│  │  │ result: 3       │    │    │  ← main的局部变量
│  │  └─────────────────┘    │    │
│  └─────────────────────────┘    │  ← main栈帧在下层
│  (addtwonum栈帧已释放)          │  ← 函数结束后自动销毁
├─────────────────────────────────┤
│  堆 (Heap)                     │
│  (本程序没用到堆,是空的)       │  ← 像空仓库
└─────────────────────────────────┘

关键结论:全局变量和局部变量的 "内存性格"

  • 全局变量:住在全局变量区,寿命和程序一样长,全程序可见(像公司前台,谁都能找她)。
  • 局部变量:住在栈上,寿命只在函数执行期间,出了函数就消失(像临时工,活儿干完就走)。
  • :本程序没用到,但如果用malloc分配内存,就会在这里安家(像长期租的仓库,不用了记得退租)。



这段代码里的extern声明纯属 "多此一举"—— 全局变量本来就能被所有函数访问,加不加extern结果都一样。就像你去用公司的打印机,没必要每次都跟同事声明 "我要用打印机",大家都知道它在那儿。

标题:

  1. 《C 代码内存解剖:全局变量住 "豪宅",局部变量住 "客栈"》
  2. 《从堆栈视角看 C 程序:全局变量的 "永久产权" 和局部变量的 "临时居住证"》

简介:

通过分析一段简单 C 代码的内存分配,揭示全局变量(存于全局区,寿命同程序)和局部变量(存于栈,寿命随函数)的本质区别,以及栈的自动管理特性,帮你理解内存中的 "演员走位"。

关键词:

#C 语言内存 #栈与堆 #全局变量 #局部变量 #内存分配

原文链接:,转发请注明来源!