C语言内存管理:栈与堆的解析

发布时间:2026/9/28 23:03:01

C语言内存管理:栈与堆的解析 任何一个程序正常运行都需要内存资源用来存放诸如变量、常量、函数代码等等。这些不同的内容所存储的内存区域是不同的且不同的区域有不同的特性。因此我们需要研究内存布局逐个了解不同内存区域的特性。每个 C 语言进程都拥有一片结构相同的虚拟内存不同的程序会占据不同区段的物理内存但经过系统的变换和映射它们的虚拟内存的布局是完全一样的。将其中一个 C 语言进程的虚拟内存放大来看会发现其内部包含区域栈stack堆heap数据段代码段1.1 栈内存栈是现代计算机程序极为重要的组成部分几乎所有的函数调用都依赖于栈进行实现。栈空间中包括函数返回地址记录了函数执行完成后退出函数调用时需要执行的下一条指令命令行参数 ./a.exe abc 123局部变量包括形参函数调用过程中使用的非静态局部变量均默认保存在栈上函数退出后这些变量也会被释放而不可使用保存的寄存器环境变量等保存了函数调用前后需要保持不变的寄存器在程序运行过程中每次执行函数调用都会建立一个栈帧连续的过程调用会让栈不断朝着“栈顶”的方向进行扩展在函数调用完成后栈帧会被自动释放栈也会随之缩减。在大多数体系结构上栈的扩展方向都是由高地址向低地址进行扩展。栈内存有什么特点空间有限尤其在嵌入式环境下。因此不可以用来存储尺寸太大的变量。Linux 栈内存大小为 8M系统为函数分配栈内存时遵循从上高地址往下低地址分配原则由系统规定的我们无法干预每当一个函数被调用时系统将自动分配一段栈内存给该函数用于存放其局部变量每当一个函数有退出时系统将自动回收其栈内存栈空间申请的变量随着函数结束空间自动释放在函数的作用域中定义局部变量是保存在栈中在函数结束时系统会自动回收此函数中创建的栈空间即 return 之后不可再访问函数的局部变量因此返回一个局部变量的地址通常是错误的。# include stdio.h int *add(int a, int b) { int sum; sum a b; return sum; //不能返回函数的局部变量地址 } int main(void) { int *p add(2,5); }1.2 堆内存堆内存heap又被称为动态内存、自由内存简称堆。堆是唯一可被开发者自定义的区段开发者可以根据需要申请内存的大小、决定使用的时间长短等。但又由于这是一块系统“飞地”所有的细节均由开发者自己把握系统不对此做任何干预给予开发者绝对的“自由”但也正因如此对开发者的内存管理提出了很高的要求。对堆内存的合理使用几乎是软件开发中的一个永恒的话题。例如 malloc 它分配的内存来自进程的虚拟内存空间。虚拟内存是操作系统提供的一种内存管理机制它允许程序使用比物理内存更多的内存堆内存基本特征相比栈内存堆的总大小仅受限于物理内存在物理内存允许的范围内系统对堆内存的申请不做限制。相比栈内存堆内存从下往上增长。堆内存是匿名的只能由指针来访问。自定义分配的堆内存除非开发者主动释放否则永不释放直到程序退出。堆内存使用完成后一定要进行手动释放内存否则可能会使内存泄漏导致程序崩溃相关API申请堆内存malloc() / calloc()功能申请堆内存所需头文件#include stdlib.h函数原型void *malloc(size_t size);void *calloc(size_t count, size_tsize);参数size所申请的一块堆内存的大小单位是字节count所申请的堆内存的块数所有的内存块是连续分布的、无间隔的返回值成功返回所申请的内存空间的指针失败NULL#include stdio.h #include stdlib.h int main() { // malloc: 申请一块内存内容是随机的 int *p1 (int *)malloc(5 * sizeof(int)); // calloc: 申请 5 块内存申请的内存会自动清零 int *p2 (int *)calloc(5, sizeof(int)); free(p1); free(p2); return 0; }清零堆内存bzero()功能清零指定内存所需头文件#include strings.h函数原型void bzero(void *s, size_t n);参数s指向要清零的内存的指针n要清零的内存大小单位是字节返回值无#include stdio.h #include stdlib.h #include strings.h int main() { int arr[5] {1, 2, 3, 4, 5}; bzero(arr, sizeof(arr)); // 整块清零 printf(bzero后 arr[0] %d, arr[4] %d\n, arr[0], arr[4]); return 0; }释放堆内存free()功能释放堆内存所需头文件#include stdlib.h函数原型voidfree(void *ptr);参数ptr堆内存指针返回值无#include stdio.h #include stdlib.h int main() { int *p (int *)malloc(3 * sizeof(int)); p[0] 10; p[1] 20; p[2] 30; printf(使用前: p[1] %d\n, p[1]); free(p); // 释放堆内存 p NULL; // 置空防止悬空指针 printf(已释放p NULL\n); return 0; }realloc()申请的堆内存,在原来内存基础上可进行扩容。// 申请堆空间 char *p calloc(1, sizeof(char)); if (p NULL) { printf(calloc failed:); return -1; } char buf[] afjoiajfajflajlfjalflafjslfjsljfls; // 扩容如果不扩容会溢出 char *ptr realloc(p, 100); // 是将空间大小从 1 扩容到 100不是扩容了 100注意malloc()申请的堆内存默认情况下是随机值一般需要用 bzero() 来清零。calloc()申请的堆内存默认情况下是已经清零了的不需要再清零。free()只能释放堆内存不能释放别的区段的内存。释放内存的含义释放内存意味着将内存的使用权归还给系统。释放内存并不会改变指针的指向。释放内存并不会对内存做任何修改更不会将内存清零。内存泄漏示例以下是一个典型的内存泄漏代码在函数中申请堆内存后忘记释放导致每次调用都泄漏内存。#include stdio.h #include stdlib.h void leak_memory() { int *p (int *)malloc(100 * sizeof(int)); // 忘记调用 free(p)函数返回后指针 p 丢失内存却未归还系统 } int main() { for (int i 0; i 100000; i) { leak_memory(); // 每次调用泄漏 400 字节累计约 40MB } printf(程序结束\n); return 0; }后果随着循环不断调用leak_memory()堆内存持续增长而永不释放。在长时间运行的程序如服务器进程中内存泄漏会逐渐耗尽可用内存最终导致系统 OOMOut of Memory或程序崩溃。即使程序结束泄漏的内存在程序运行期间也无法被其他进程使用。如何避免配对使用 malloc 和 free每次调用malloc后确保在合适的时机调用free。使用内存检测工具如 ValgrindLinux或 Dr. MemoryWindows检测内存泄漏。遵循谁申请谁释放原则明确内存的所有权和释放责任避免遗漏。在函数退出前统一释放用goto或if分支确保所有路径都能释放已申请的内存。1.3 堆与栈的区别1、 管理方式不同:栈由系统自动分配空间同时系统自动释放空间。堆需要程序员手动申请并且手动释放并指明大小。在 C 语言中 malloc 函数申请释放 free 函数在 C 中 new 和 delete 实现。2、 存储内容不同:栈在函数调用时栈中存放的是函数中各个参数(局部变量)堆一般是在堆的头部用一个字节存放堆的大小不是必须的。堆中的具体内容由程序员安排。3、 空间大小不同:栈获取空间较小。堆比较灵活比较大。4、 能否产生碎片不同:栈不会产生碎片空间连续。堆采用的是链表的存储方式会产生碎片。5、 分配方式不同:栈静态分配由编译器完成堆动态分配的使用malloc函数6、 分配效率不同栈由系统自动分配速度较快。但程序员是无法控制的。堆由malloc分配的内存一般速度比较慢而且容易产生内存碎片不过用起来方便。7、 生长方向不同栈向低地址扩展的数据结构是一块连续的内存的区域。堆向高地址扩展的数据结构是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的自然是不连续的而链表的遍历方向是由低地址向高地址。
延伸阅读

更多相关文章

2026/9/24 6:33:37

3分钟搞定硬字幕提取:本地免费视频字幕提取神器完全指南

3分钟搞定硬字幕提取:本地免费视频字幕提取神器完全指南 【免费下载链接】video-subtitle-extractor 视频硬字幕提取,生成srt文件。无需申请第三方API,本地实现文本识别。基于深度学习的视频字幕提取框架,包含字幕区域检测、字幕内…

2026/9/27 20:46:36

谷歌15亿美元收购AI编程初创公司,AI编程进入工程化时代

如果你是一名开发者,最近可能已经感受到了AI编程工具带来的效率冲击。从GitHub Copilot到Cursor,再到各种本地化模型,AI辅助编程正在从“玩具”变成“生产力工具”。但你是否想过,当这些工具背后的技术被科技巨头整合,…

2026/9/27 5:23:46

Spark SQL distinct操作性能优化实战指南

1. Spark SQL中distinct操作的性能瓶颈解析在Spark SQL的实际应用中,distinct操作是数据去重的常见需求,但也是最容易引发性能问题的操作之一。我曾在多个大数据项目中处理过distinct导致的作业卡顿问题,发现大多数情况下性能瓶颈都源于对dis…

2026/9/28 22:59:00

Agent提示词模板管理与多Agent编排实战:从硬编码到可维护架构

1. 从硬编码到模板化:提示词管理的分水岭如果你写过超过三个 Agent 项目,大概率经历过这样的场景:同一个系统提示词在五个文件里各有一份,改了一处忘了另外四处;产品经理说"把客服 Agent 的语气调得再温和一点&qu…

2026/9/28 22:59:00

CANoe 12.0 Trace窗口筛选栏空白原因排查与解决方法

CANoe 12.0的Trace窗口筛选栏空白,这个问题乍一看不大,但真碰上能把人折腾够呛。我最初遇到的时候正在排查一组CAN报文,急着按ID过滤看周期,结果筛选栏整条消失,鼠标点过去连输入框都唤不出来,那叫一个难受…

2026/9/28 22:59:00

C语言顺序表从零实现:动态扩容、插入删除与踩坑指南

顺序表这个词,很多刚学完C语言、第一次翻开数据结构教材的人看到它,第一反应就是“这不就是数组嘛”。但真到了自己动手实现的时候,才发现事情远没那么简单:插入位置永远差一个数、函数里明明改了值外面却没变、跑着跑着内存就炸了…

2026/9/28 22:59:00

AgentScope多智能体协作实战:从踩坑到跑通完整流程

1. 为什么我会盯上 AgentScope 这个框架第一次听到 AgentScope 这个名字,是在一个做多智能体协作的朋友群里。当时有人甩了一句“这玩意儿比手搓 LangChain 链路省心多了”,我还没太当回事。直到我自己接手了一个需要多个智能体分工协作的项目——一个负…

2026/9/28 22:54:00

Agent-Native:从传统应用到智能体原生的架构迁移与落地

1. agent-native是什么:一场应用架构的迁移1.1 一个反例:为什么“能聊天”不等于“智能体原生”最近圈子里到处都在聊agent-native。这个词直译过来是“智能体原生”,指的是一种全新的应用架构取向:从架构、数据流、交互界面到运维…

2026/9/28 3:03:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/28 6:05:15

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/28 6:07:41

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/28 0:02:03

广州外贸网站建设推广:从零搭建全流程拆解与真实报价避坑

广州外贸网站建设推广:从零搭建全流程拆解与真实报价避坑 改个需求建站公司拖一周,后台改个文案还得再交一笔“技术维护费”。这种憋屈事儿,做外贸的朋友太熟悉了。很多老板在找广州外贸网站建设推广服务商时,光盯着首页好不好看,却忽略了从零搭建一个能…

2026/9/28 0:02:04

搞懂百度竞价推广价格,网站性能优化别掉链子

搞懂百度竞价推广价格,网站性能优化别掉链子 网站突然打不开,浏览器弹出红色警告“此网站存在安全风险”,后台一看全是乱码代码和奇怪的跳转链接。这种网站被黑挂马的绝望感,很多刚转行做网站的朋友都经历过,尤其是那些为了省几百块钱服务器费用的新手。…

2026/9/25 20:55:38

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/26 19:58:38

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/28 1:59:25

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑