当前位置: 首页 > news >正文

堆内存、栈内存、内存地址

一、堆(Heap)

  • 用途:存储动态分配的内存,比如通过 new 创建的对象。

  • 特点

    • 生命周期由程序控制(在 C# 中由垃圾回收器 GC 管理)。

    • 内存较大,但分配/释放效率比栈慢。

    • 所有引用类型的实例(如类)都在堆上创建。

  • 示例(C#)

class Person { public string Name; }var p = new Person(); // p 是引用,指向堆上的对象

 二、栈(Stack)

  • 用途:用于存储函数调用过程中的局部变量、参数等。

  • 特点

    • 生命周期短:函数执行完毕后自动释放。

    • 分配速度快,先进后出(FILO)结构。

    • 值类型变量(如 int, bool, struct)如果是局部变量,通常存在栈上。

  • 示例(C#)

void Test() {int a = 10; // a 是值类型,存在栈上
}

 三、常见误解:“堆栈(Heap Stack)”

有时“堆栈”这个词在中文环境下被混用或误称,其实它应该具体说是“堆(Heap)”或“栈(Stack)”,不要把它们合成一个词理解为一个东西。

四、对比总结表:

对比项堆(Heap)栈(Stack)
管理方式手动或自动(GC)自动释放
存储内容引用类型对象、数组值类型变量、函数调用上下文
分配速度
生命周期程序控制/GC回收方法调用期间
内存大小较大较小


如你用 C# 写代码,可以记住一句话:

值类型在栈,引用类型在堆(值在栈上,引用在堆上)(有些结构体情况略复杂,但大体如此)。

五、拓展:数组是值类型还是引用类型?

char[] a = new char[] { 'a', 'b', 'c' };
  • char[]引用类型(即使它内部装的是值类型 char)。

  • 数组本身存储在堆上

  • 变量 a 是一个引用(指针),它本身存在栈上,指向堆中的数组数据。


🧩 内存结构分析

对于这段代码:

char[] a = new char[] { 'a', 'b', 'c' };

内存中大概是这样:

栈(Stack)

a -> [ 堆上的数组地址 ]

堆(Heap)

数组对象 header(元数据)
['a', 'b', 'c'] ← 连续的 char 值(每个是 2 字节)

再看看这个例子 

void Test()
{int[] nums = new int[] { 1, 2, 3 }; // 数组对象在堆上,nums 是引用,在栈上int x = nums[0];                   // x 是值类型,值拷贝存在栈上
}
  • nums 是引用类型,在栈上;

  • nums 指向堆上的 int[] 数组;

  • x 是值类型,直接在栈上存放拷贝的 1


📝 总结

类型值/引用类型存储位置
int值类型
char值类型
int[]引用类型数组本体在堆上
char[]引用类型数组本体在堆上
string引用类型堆(字符串是不可变对象)

补充:

一、C# 数组对象在堆上的结构大致如下:

以 char[] a = new char[] { 'a', 'b', 'c' }; 为例
注意:char 是 Unicode,每个占 2 字节
[ 栈 Stack ]
-----------------------
a (char[] 引用)
↓
0x0012F3A0   ← 地址,指向堆[ 堆 Heap ]
--------------------------------------
地址:0x0012F3A0
+------------------------+
| 对象头(Object Header)| ← 8 字节(64 位系统)
+------------------------+
| 方法表指针(MethodTbl)| ← 8 字节(指向类型元数据)
+------------------------+
| 数组长度:3            | ← 4 字节(int 类型)
+------------------------+
| 元素1:'a'             | ← 2 字节(char = 2B)
+------------------------+
| 元素2:'b'             | ← 2 字节
+------------------------+
| 元素3:'c'             | ← 2 字节
+------------------------+

🖼️ 二、图示(ASCII 简化图)

[ Stack ]
+-------------------+
| a (变量名)        |
| 0x0012F3A0        | → 指向堆
+-------------------+[ Heap ]
地址: 0x0012F3A0
+-----------------------------+
| Object Header   (8 bytes)  | ← CLR 控制的同步/GC 等信息
+-----------------------------+
| Method Table Ptr (8 bytes) | ← 指向类型描述,比如 System.Char[]
+-----------------------------+
| Array Length = 3 (4 bytes) |
+-----------------------------+
| 'a' (0x0061)  (2 bytes)     |
| 'b' (0x0062)  (2 bytes)     |
| 'c' (0x0063)  (2 bytes)     |
+-----------------------------+

📌 三、说明:

  • Object Header:CLR 内部用来记录同步锁、GC 状态等,不对用户可见;

  • Method Table Pointer:指向数组的类型信息,用于调用虚方法或进行类型检查;

  • Length 字段:数组的长度,是 System.Array 的字段;

  • 数组元素:紧随长度字段后,按顺序存储,内存上是连续的

  • 所以你可以用 fixed 获取数组首元素地址并按偏移读取。


四、在 Visual Studio 中查看变量地址的方法

方法 1:在“监视”窗口中使用 &变量fixed

⚠️ 注意:只有值类型变量(如 int, char)才能直接取地址
引用类型要通过间接方式(如 GCHandlefixed

unsafe
{char[] arr = new char[] { 'a', 'b', 'c' };fixed (char* p = arr){Console.WriteLine((ulong)p); // 设置断点这里}
}

 如图:

NameValueType
p0x000001a5828fe8b0char*
&arr30x000000789477eb70System.Char[]*

 疑问:p和arr3的地址怎么不一样?

你的观察非常敏锐,这是一个很多人调试 C# 时的疑惑点。

你看到类似这样的情况:

&arr3  = 0x000000789477eb70   // arr3 的引用变量地址(在栈上)
p      = 0x000001a5828fe8b0   // p 是 fixed 后数组元素的地址(在堆上)

✅ 为什么两个地址不一样?

因为:

  • &arr3引用变量的地址(在栈上)
    👉 它表示“arr3 这个变量本身存在的位置”,也就是栈中的地址

  • p数组首元素(arr3[0])的地址(在堆上)
    👉 它是 char[] 数组本体中第一个字符的实际地址,也就是堆中的地址

 类比解释(图像化):

[栈 stack]
+----------------------+
| arr3 (变量名)        | ← &arr3(变量自身的地址)
| 值:0x000001a5828fe8b0  ← 这是堆上数组的地址
+----------------------+[堆 heap]
0x000001a5828fe8b0 → 数组对象头 + 长度字段 + 'a' 'b' 'c'↑p (指向 arr3[0])

所以:

  • &arr3:是栈中 arr3 这个引用变量本身的地址(存储在栈上)

  • arr3 的值:是一个指向堆上数组对象的地址

  • p:是 fixed (char* p = arr3) 后,固定在堆上的数组首元素地址


方法 2、使用 “内存窗口” 查看内存中的内容

步骤:

  1. 设置断点后运行程序

  2. 命中断点后,点击菜单:

    • 调试 > Windows > 内存 > 内存1(也可以按 Ctrl+Alt+M, 1

  3. 在地址栏输入:p 或者 0x02F3A210(你从监视窗口看到的地址)

  4. 回车,就能看到数组的内存内容:

0x000001A5828FE8B0  61 00 62 00 63 00 00 00 00 00 00 00 00 00 00 00 28 66 27 e9 fa 7f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  a.b.c...........(f'??...........................

如图:


⚙️ 五、如何启用 unsafe 支持?

如果你的项目不能编译,可以这样做:

✅ 方法 1:修改项目属性(推荐)

  1. 右键项目 → 属性

  2. 选择“生成”选项卡

  3. 勾选 “允许不安全代码”

  4. 保存后重新编译

✅ 方法 2:手动编辑 .csproj 文件:

<PropertyGroup><AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

 


✅ 总结

关键词含义
unsafe开启不安全上下文,允许指针操作
fixed固定托管对象在内存中,防止被 GC 移动
char* p获取数组首元素(arr[0])的地址

仅供学习参考,如有侵权联系我删除


文章转载自:
http://broadband.hfytgp.cn
http://brighish.hfytgp.cn
http://cataplexy.hfytgp.cn
http://blanketyblank.hfytgp.cn
http://cannonize.hfytgp.cn
http://anchorless.hfytgp.cn
http://boating.hfytgp.cn
http://chainlet.hfytgp.cn
http://bootlast.hfytgp.cn
http://ambitiously.hfytgp.cn
http://avianize.hfytgp.cn
http://bohea.hfytgp.cn
http://acetifier.hfytgp.cn
http://baffler.hfytgp.cn
http://audiovisual.hfytgp.cn
http://astringe.hfytgp.cn
http://blacking.hfytgp.cn
http://chalcidian.hfytgp.cn
http://beedie.hfytgp.cn
http://appellee.hfytgp.cn
http://amplexus.hfytgp.cn
http://assertion.hfytgp.cn
http://assessor.hfytgp.cn
http://chairlady.hfytgp.cn
http://cemf.hfytgp.cn
http://automorphism.hfytgp.cn
http://cecal.hfytgp.cn
http://airbrush.hfytgp.cn
http://christopher.hfytgp.cn
http://catholicise.hfytgp.cn
http://www.dtcms.com/a/281212.html

相关文章:

  • 作业:复制数组
  • EndNote
  • 【Keil】C/C++混合编程的简单方法
  • DGNNet:基于双图神经网络的少样本故障诊断学习模型
  • 深入浅出 RabbitMQ-核心概念介绍与容器化部署
  • Element plus参考vben逻辑实现的描述列表组件封装实践
  • 【PTA数据结构 | C语言版】二叉树前序序列化
  • 差分信号接口选型指南:深入解析LVDS、SubLVDS、SLVDS与SLVDS-EC**
  • 《大数据技术原理与应用》实验报告五 熟悉 Hive 的基本操作
  • [AI8051U入门第三步]串口1使用-printf重定向(乱码解决办法)
  • Django+Celery 进阶:动态定时任务的添加、修改与智能调度实战
  • Android target34升级到35中的edge-to-edge适配
  • Nestjs框架: 数据库架构设计与 NestJS 多 ORM 动态数据库应用与连接池的配置
  • 利用android studio,对图片资源进行二次压缩
  • 基于Ruoyi和PostgreSQL的统一POI分类后台管理实战
  • 三步把餐饮回访差评变口碑
  • java+vue+SpringBoot在线租房和招聘平台(程序+数据库+报告+部署教程+答辩指导)
  • Ajax原理、用法与经典代码实例
  • TCP协议可靠性设计的核心机制与底层逻辑
  • 基于YOLOv8的水稻叶片病害检测系统的设计与实现【近6W条数据集+多病害特征+高准确率】
  • 修改系统配置后,如何编写 Python 脚本以适应 SSL 证书验证的变化
  • Axios 和 Promise 区别对比
  • C语言---自定义类型(下)(枚举和联合类型)
  • 利用DeepSeek为chdb命令行客户端添加输出重定向和执行SQL脚本功能
  • nlp论文:分本分类:《Bag of Tricks for Efficient Text Classification》
  • VirtualBox网络配置全指南:桥接、Host-Only与双网卡实战
  • 2025华为ODB卷-士兵过河-三语言题解
  • 限制apk使用时长第二篇-限制/拦截Apk启动-应用锁功能
  • 创客匠人:创始人 IP 变现,从 “单点尝试” 到 “生态赋能” 的跨越
  • S7-200 SMART PLC:不同CPU及数字量 IO 接线全解析