栈内存 (Stack Memory) 和 堆内存 (Heap Memory)
lipiwang 2025-05-15 19:00 8 浏览 0 评论
C 语言(以及很多其他编程语言)中非常重要的两个内存区域:栈内存 (Stack Memory) 和 堆内存 (Heap Memory)。
程序运行时,需要内存来存储指令、变量、对象等数据。操作系统会为每个运行的程序分配一块内存空间,这块空间通常会被划分为几个不同的区域,其中栈和堆是用于存储程序数据的两个最核心的区域。
一、栈内存 (Stack Memory)
* 定义与用途:
* 栈是一块内存区域,主要用于存储函数调用信息(如返回地址、参数)和局部变量。
* 它的运作方式遵循后进先出 (Last-In, First-Out, LIFO) 的原则。
* 工作机制:
* 函数调用: 当一个函数被调用时,系统会在栈顶创建一个新的栈帧 (Stack Frame)。
* 栈帧内容: 这个栈帧包含了函数的参数、函数的返回地址(调用结束后程序应该回到哪里)、以及函数内部定义的局部变量。
* 函数返回: 当函数执行完毕并返回时,对应的栈帧会从栈顶弹出 (pop),其中存储的所有局部变量和参数都会被销毁,内存被自动释放。
* 嵌套调用: 如果函数 A 调用了函数 B,那么函数 B 的栈帧会叠在函数 A 的栈帧之上。当 B 返回时,B 的栈帧弹出;当 A 返回时,A 的栈帧弹出。
* 特点:
* 自动管理: 栈内存的分配和释放是由编译器(或运行时系统)自动管理的,程序员不需要手动干预。分配和释放速度非常快。
* 大小固定: 每个线程的栈大小通常在程序启动时(或线程创建时)就确定了,相对较小(例如,在某些系统上可能是几 MB)。
* 生命周期: 栈上变量的生命周期与它们所在的作用域(通常是函数体或代码块 {}) 绑定。一旦离开作用域,变量就会被自动销毁。
* 高效访问: 栈顶指针(Stack Pointer register)通常由 CPU 直接管理,访问栈内存非常快。
* 数据连续: 栈帧内的局部变量通常是连续存储的。
* 存储内容示例:
* 函数参数
* 函数内部定义的局部变量(如 int a;, char buffer[100];)
* 函数调用的返回地址
* 一些寄存器的状态
* 潜在问题:
* 栈溢出 (Stack Overflow): 如果函数调用嵌套过深(例如,无限递归或非常深的递归),或者在函数内定义了过大的局部变量(尤其是大数组),超出了栈的容量限制,就会发生栈溢出,导致程序崩溃。
二、堆内存 (Heap Memory)
* 定义与用途:
* 堆是另一块内存区域,用于动态内存分配 (Dynamic Memory Allocation)。当程序需要在运行时根据需要分配内存,并且希望这些内存在函数调用结束后仍然存在时,就会使用堆。
* 堆的大小不像栈那样固定,它通常比栈大得多,受限于计算机的可用虚拟内存。
* 工作机制:
* 手动管理: 堆内存的分配和释放完全由程序员手动控制。
* 分配: 使用 malloc(), calloc(), realloc() 等库函数向操作系统申请一块指定大小的内存。这些函数会在堆中寻找一块足够大的空闲空间,标记为已使用,并返回指向这块内存起始地址的指针。
* 释放: 当不再需要这块内存时,程序员必须调用 free() 函数,并将之前 malloc 等函数返回的指针传递给它,以将内存归还给系统,使其可以被再次分配。
* 特点:
* 灵活大小: 可以在运行时请求任意大小的内存块(只要系统能提供)。
* 生命周期控制: 堆上分配的内存的生命周期由程序员决定,从 malloc 调用开始,直到显式调用 free 结束。它不受函数作用域的限制,可以在一个函数中分配,在另一个函数中使用和释放。
* 较慢的分配/释放: 相比栈的自动管理,堆的分配(需要查找合适的空闲块)和释放(可能需要合并相邻的空闲块)通常更耗时。
* 内存碎片 (Fragmentation): 频繁地分配和释放不同大小的内存块可能导致堆中出现许多不连续的小空闲块。即使总的空闲内存足够,也可能找不到一个足够大的连续块来满足某个较大的分配请求,这就是内存碎片问题。
* 访问速度: 访问堆内存通常需要通过指针间接访问,可能比直接访问栈上的局部变量稍慢,并且由于内存可能不连续,缓存命中率可能较低。
* 存储内容示例:
* 需要跨函数边界共享的大型数据结构(如链表节点、树节点)。
* 大小在编译时未知,需要在运行时确定的数组或对象。
* 需要长时间存在的数据。
* 潜在问题:
* 内存泄漏 (Memory Leaks): 如果分配了堆内存 (malloc) 但忘记在不再需要时释放它 (free),这部分内存就无法被再次使用,导致可用内存逐渐减少,最终可能耗尽系统资源。这是堆内存管理中最常见的问题。
* 悬挂指针 (Dangling Pointers): 当指向的堆内存已经被 free 释放后,如果仍然尝试通过该指针访问或再次 free 这块内存,就会产生未定义行为(通常导致程序崩溃)。
* 重复释放 (Double Free): 对同一块堆内存调用两次 free 也会导致未定义行为。
三、栈 vs. 堆 - 关键区别总结
| 特性 | 栈内存 (Stack) | 堆内存 (Heap) |
|---|---|---|
| 管理方式 | 自动 (编译器/运行时) | 手动 (程序员) |
| 分配/释放 | 非常快 (LIFO, 指针移动) | 较慢 (malloc/free 涉及查找、管理) |
| 生命周期 | 与作用域绑定 (函数/代码块) | 程序员控制 (从 malloc 到 free) |
| 大小 | 固定,相对较小 | 灵活,通常较大,受限于系统内存 |
| 碎片问题 | 无 | 可能产生内存碎片 |
| 访问速度 | 快 | 相对较慢 (通过指针间接访问) |
| 主要用途 | 函数调用、局部变量 | 动态分配、全局共享、大对象、长生命周期数据 |
| 主要风险 | 栈溢出 (Stack Overflow) | 内存泄漏 (Memory Leak), 悬挂指针, 重复释放 |
总结:
* 栈 就像是餐馆里给你快速放盘子、收盘子的地方,自动、快速、有固定容量,盘子(数据)用完就立即收走。适合存放临时的、函数内部使用的数据。
* 堆 就像是一个大仓库,你需要自己去申请一块地方(malloc),自己决定放多久,用完后还要自己去告诉管理员可以回收了(free)。适合存放需要灵活大小或需要在程序不同部分长期共享的数据。
理解栈和堆的区别对于编写高效、健壮的 C 程序至关重要,尤其是在处理内存管理时。你需要根据数据的生命周期、大小和访问需求来决定将数据放在栈上还是堆上。
- 上一篇:C++/C入门之堆和栈
- 下一篇:架构师面试之-------谈谈jvm中堆和栈的区别
相关推荐
- 前端入门——css 网格轨道详细介绍
-
上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...
- Islands Architecture(孤岛架构)在携程新版首页的实践
-
一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...
- HTML中script标签中的那些属性
-
HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...
- CSS 中各种居中你真的玩明白了么
-
页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...
- CSS样式更改——列表、表格和轮廓
-
上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...
- 一文吃透 CSS Flex 布局
-
原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...
- css实现多行文本的展开收起
-
背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...
- css 垂直居中的几种实现方式
-
前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...
- WordPress固定链接设置
-
WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...
- 面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿
-
前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...
- 3种CSS清除浮动的方法
-
今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...
- 2025 年 CSS 终于要支持强大的自定义函数了?
-
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...
- css3属性(transform)的一个css3动画小应用
-
闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...
- CSS基础知识(七)CSS背景
-
一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...
- CSS 水平居中方式二
-
<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)