从栈和堆中来看值传递和引用传递

本文主要介绍了C#中栈和堆的概念,栈是后进先出的数据结构,编译期分配内存;堆可动态分配内存。还阐述了值类型和引用类型在内存中的存储方式,以及值参数和引用参数的传递机制,包括值类型和引用类型作为不同参数传递的示例和分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击上方关注 “终端研发部


 
设为“星标”,和你一起掌握更多数据库知识

来源:本文来自小小罗CR7的投稿

blog.youkuaiyun.com/sinat_32832727/article/details/54986644#


1、栈和堆

 栈

栈是一个内存数组,是一个LIFO(Last-In First-Out,后进先出)的数据结构。

栈存储几种类型的数据:

  • 某些类型变量的值

  • 程序当前的执行环境

  • 传递给方法的参数

栈有如下几个特征:

  • 数据只能从栈的顶端插入和删除。

  • 把数据放到栈顶称为入栈(push)

  • 从栈顶删除数据称为出栈(pop)

  • 栈是在编译期,由编译器分配好内存空间。


 堆

堆是一块内存区域,在堆中可以分配大块的内存用于存储某类型的数据对象。

与栈不同,堆里的内存能够以任意顺序存入和删除。

堆是在运行期,根据程序运行情况动态分配内存空间。

2、值类型和引用类型

C#数据类型分为两种:值类型和引用类型,这两种类型的对象在内存中的存储方式不同。

值类型:只需要一段单独的内存,用于存储实际的数据。

引用类型:需要两段内存。

第一段存储实际的数据,它总是位于堆中。

第二段是一个引用,指向数据在堆中的存放位置。

一般情况下,对于值类型,数据存放在栈里。对于引用类型,引用存放在栈里,实际数据存放在堆里。如图所示:

a2143f206a716deb02b6d394634c7336.jpeg

当然,上面是一般情况,值类型数据或引用类型的引用部分也会存放在堆中。

比如,当一个值类型或一个引用类型是另一个引用类型对象的数据时,便会存放在堆中。

C#中的值类型和引用类型如图所示:

19da9a14444643edbb96a06e0ca168b2.png

3 值参数和引用参数(值传递和引用传递)

值参数:通过将实参的值复制到形参的方式把数据传递给方法。方法被调用时,系统在栈中为形参分配空间,然后将实参的值复制给形参。

引用参数:在参数前面加 ref 或 out 修饰符 ,实参必须是变量。

ref 和 out 的区别:ref参数传入方法之前必须初始化;out参数不需要,但out参数在方法返回时必需赋值。

3.1 值类型数据作为值参数传递

using System;  

namespace Test  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            int val = 10;  

            MyMethod(val);  

            Console.WriteLine("val的值为:{0}",val);  

        }  

        static void MyMethod(int xxx)  

        {  

            xxx = xxx + 5;  

            Console.WriteLine("xxx的值为:{0}",xxx);  

        }  

    }  

}  

结果如图所示:

d2a948b9d56701e857a44911443462d0.png


分析如图所示:

dc21d064aa798e36c6d437fc723abce8.png


3.2 值类型数据作为引用参数传递

using System;  

namespace Test  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            int val = 10;  

            MyMethod(ref val);  

            Console.WriteLine("val的值为:{0}",val);  

        }  

        static void MyMethod(ref int xxx)  

        {  

            xxx = xxx + 5;  

            Console.WriteLine("xxx的值为:{0}",xxx);  

        }  

    }  

}  

结果如图所示:

06266c363e592266955b2e4aa5351a20.png

分析如图所示:

883d4fc9b9a70bf37e9ed6aa299e1550.png


3.3 引用类型数据作为值参数传递

写了两个例子,注意在方法内部形参p的不同之处!

示例1:

using System;  

namespace Test  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            Person p1 = new Person();  

            p1.name = "lzj";  

            MyMethod(p1);  

            Console.WriteLine("p1.name的值为:{0}",p1.name);  

        }  

        static void MyMethod(Person p)  

        {  

            p.name = "CR7";  

            Console.WriteLine("p.name的值为:{0}",p.name);  

        }  

    }  

    class Person  

    {  

        public string name;  

    }  

}  

结果如图所示:

3026881c9682c1516f4a56e3443ef055.png

分析如图所示:

bc1bd1f0be9b218d42775ac82a04cb5a.png

示例2:

using System;  

namespace Test  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            Person p1 = new Person();  

            p1.name = "lzj";  

            MyMethod(p1);  

            Console.WriteLine("p1.name的值为:{0}",p1.name);  

        }  

        static void MyMethod(Person p)  

        {  

            p = new Person() { name = "CR7" };  

            Console.WriteLine("p.name的值为:{0}",p.name);  

        }  

    }  

    class Person  

    {  

        public string name;  

    }  

}  

结果如图所示:

e2c2824fe68471f5fbfc1f517dc4a3fd.png

分析如图所示:


4b301a201795968038c7e860bd5d887f.png


3.4 引用类型数据作为引用参数传递

using System;  

namespace Test  

{  

    class Program  

    {  

        static void Main(string[] args)  

        {  

            Person p1 = new Person();  

            p1.name = "lzj";  

            MyMethod(ref p1);  

            Console.WriteLine("p1.name的值为:{0}",p1.name);  

        }  

        static void MyMethod(ref Person p)  

        {  

            p = new Person() { name = "CR7" };  

            Console.WriteLine("p.name的值为:{0}",p.name);  

        }  

    }  

    class Person  

    {  

        public string name;  

    }  

}  

结果如图所示:

86d8af378a50cab3c9dd07ac52ede01c.png

分析如图所示:

16860851c44234dab23159b08ab7a698.png


总结

作为值参数(即值传递)时:传递的是栈中的数据。

作为引用参数(即引用传递时):传递的是栈本身的地址。

最后说一句(别白嫖,求关注)

回复 【idea激活】即可获得idea的激活方式
回复 【Java】获取java相关的视频教程和资料
回复 【SpringCloud】获取SpringCloud相关多的学习资料
回复 【python】获取全套0基础Python知识手册
回复 【2020】获取2020java相关面试题教程
回复 【加群】即可加入终端研发部相关的技术交流群
阅读更多
用 Spring 的 BeanUtils 前,建议你先了解这几个坑!

lazy-mock ,一个生成后端模拟数据的懒人工具

在华为鸿蒙 OS 上尝鲜,我的第一个“hello world”,起飞!

字节跳动一面:i++ 是线程安全的吗?

一条 SQL 引发的事故,同事直接被开除!!

太扎心!排查阿里云 ECS 的 CPU 居然达100%

一款vue编写的功能强大的swagger-ui,有点秀(附开源地址)


相信自己,没有做不到的,只有想不到的在这里获得的不仅仅是技术!



喜欢就给个“在看”
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值