.NET的堆和栈02,值类型和引用类型参数传递以及内存分配

在" .NET的堆和栈01,基本概念、值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配。我们知道:当执行一个方法的时候,值类型实例会在"栈"上分配内存,而引用类型实例会在"堆"上分配内存,当方法执行完毕,"栈"上的实例由操作系统自动释放,"堆"上的实例由.NET Framework的GC进行回收。而本篇的重点要放在:值类型和引用类型参数的传递,以及内存分配。

主要包括:
■  传递值类型参数
■  传递容易造成"栈溢出"的值类型参数,在值类型参数前加关键字ref
■  传递引用类型参数
■  传递引用类型参数,在引用类型参数之前加关键字ref

传递值类型参数

class Class1
     {
          public void Go()
          {
              int x = 5;
              AddFive(x);

              Console.WriteLine(x.ToString());

          }

          public int AddFive(int pValue)
          {
              pValue += 5;
              return pValue;
          }
     }

大致过程如下:

1、值类型变量x被放到"栈"上。

2、开始执行AddFive()方法,值类型变量pValue被放到"栈"上,并把x的值赋值给pValue,pValue的值变成了5。

3、继续执行AddFive()方法,pValue的值变成了10。

4、执行完AddFive()方法,释放pValue的内存,"栈"指针回到x,线程重新回到Go()方法中。

输出结果:5

以上,在传递值类型参数x的时候,实际上是把x一个字节一个字节地拷贝给pValue。

传递容易造成"栈溢出"的值类型参数,在值类型参数前加关键字ref

public struct MyStruct
           {
               long a, b, c, d, e, f, g, h, i, j, k, l, m;
           }

          public void Go()
          {
             MyStruct x = new MyStruct();
             DoSomething(x);

          }

           public void DoSomething(MyStruct pValue)
           {
                    // DO SOMETHING HERE....
           }

假设以上的值类型struct足够大,而x和pValue都会被分配到"栈"上,这时可能造成"栈溢出"。

如何避免呢?
--解决办法是让DoSomething传递一个ref类型参数。这样写:

public struct MyStruct
           {
               long a, b, c, d, e, f, g, h, i, j, k, l, m;
           }

          public void Go()
          {
             MyStruct x = new MyStruct();
             x.a = 5;
             DoSomething(ref x);

             Console.WriteLine(x.a.ToString());

          }

          public void DoSomething(ref MyStruct pValue)
          {
                   pValue.a = 12345;
          }

使用ref后,执行DoSomething(ref x),是把x的地址赋值给了pValue,即pValue和x指向了同一个引用地址。当改变pValue的值,变化也会反映到x中。

输出结果:12345

以上,为了避免"大型"值类型参数传递时造成的"栈溢出",可以在值类型前面加ref关键字,于是,在传递值类型参数x的时候,实际上是把x本身的栈地址拷贝给pValue,x和pValue指向同一个栈地址。

传递引用类型参数

传递引用类型参数的道理和在传递的值类型参数前面加ref关键字是一样的。

public class MyInt
           {
               public int MyValue;
           }

          public void Go()
          {
             MyInt x = new MyInt();
             x.MyValue = 2;

             DoSomething(x);

             Console.WriteLine(x.MyValue.ToString());

          }

           public void DoSomething(MyInt pValue)
           {
               pValue.MyValue = 12345;
           }

输出结果:12345

以上大致过程是这样:
1、在托管堆上创建一个MyInt类型的实例
2、在栈上创建一个MyInt类型的变量x指向堆上的实例
3、把托管堆上的公共字段MyValue赋值为2
4、通过DoSomething(x)方法,把x的引用地址赋值给pValue,即pValue和x指向同一个引用地址
5、改变pValue的值,也会反映到x上

以上,在传递引用类型参数x的时候,实际上是把x指向托管堆实例的引用地址拷贝给pValue,x和pValue指向同一个托管堆实例地址。

传递引用类型参数,在引用类型参数之前加关键字ref

public class Thing
           {
           }

           public class Animal:Thing
           {
               public int Weight;
           }

           public class Vegetable:Thing
           {
               public int Length;
           }

          public void Go()
          {
             Thing x = new Animal();

             Switcharoo(ref x);

              Console.WriteLine(
                "x is Animal    :   "
                + (x is Animal).ToString());

              Console.WriteLine(
                  "x is Vegetable :   "
                  + (x is Vegetable).ToString());

          }

           public void Switcharoo(ref Thing pValue)
           {
               pValue = new Vegetable();
           }

输出结果:
x is Animal    :   False
x is Vegetable :   True

以上大致过程是这样:
1、在托管堆上创建Animal对象实例。
2、在栈上创建类型为Thing的x变量指向Animal实例的引用地址。
3、通过Switcharoo(ref x)方法把x本身的地址赋值给pValue,至此,pValue和x指向了相同的栈内存地址,任何一方的变化都会反映到另外一方。

4、在Switcharoo(ref Thing pValue)内部,在托管堆上创建Vegetable对象实例。
5、pValue指向Vegetable实例,也就相当于x指向Vegetable实例。

以上,当在引用类型参数之前加上关键字ref,再传递,是把x本身的栈地址拷贝给pValue,x和pValue指向同一个栈地址。

参考资料:
C# Heap(ing) Vs Stack(ing) in .NET: Part II
《你必须知道的.NET(第2版)》,作者王涛。

".NET的堆和栈"系列包括:

.NET的堆和栈01,基本概念、值类型内存分配

.NET的堆和栈02,值类型和引用类型参数传递以及内存分配

.NET的堆和栈02,值类型和引用类型参数传递以及内存分配

时间: 2024-11-05 06:14:20

.NET的堆和栈02,值类型和引用类型参数传递以及内存分配的相关文章

JVM基础概念总结:数据类型、堆与栈、基本类型与引用类型

Java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置. 基本类型包括:byte, short, int, long, char, float, double, Boolean, returnAddress 引用类型包括:类类型,接口类型和数组. 堆与栈 堆和栈是程序运行的关键,很有必要把他们的关系说清楚. 栈是运行时的

02值类型、引用类型、字符串

值类型均隐式派生自 System.ValueType 数值类型.bool.结构.枚举 引用类型派生自 System.Object 字符串.数组.类.接口等 引用类型变量的赋值只复制对对象的引用 值类型变量赋值会拷贝一个副本 常用类库string 字符串不可变性,字符串的“暂存池”两个特性 String字符串,字符串可以看成字符数组,不可变性 StringBuilder StringBuilder高效的字符串操作 当进行大量的字符串操作的时候,比如,很多次的字符串拼接操作. String 对象是不

.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)

作者: Edison Chou  来源: 博客园  发布时间: 2014-09-03 15:59  阅读: 318 次  推荐: 2   原文链接   [收藏] 原文作者:Shivprasad koirala 英文原文:Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing 一.概述 本文会阐述六个重要的概念:堆.栈.值类型.引用类型.装箱和拆箱.本文首先会通过阐述当

.NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

内容导读 概述 当你声明一个变量背后发生了什么? 堆和栈 值类型和引用类型 哪些是值类型,哪些是引用类型? 装箱和拆箱 装箱和拆箱的性能问题 一.概述 本文会阐述六个重要的概念:堆.栈.值类型.引用类型.装箱和拆箱.本文首先会通过阐述当你定义一个变量之后系统内部发生的改变开始讲解,然后将关注点转移到存储双雄:堆和栈.之后,我们会探讨一下值类型和引用类型,并对有关于这两种类型的重要基础内容做一个讲解. 本文会通过一个简单的代码来展示在装箱和拆箱过程中所带来的性能上的影响,请各位仔细阅读. 二.当你

【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱

为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个菜比,弱爆了!),所以肯定会有很多问题(有些语句理解不透彻,翻译出来也不通顺,还请不吝赐教),也请各位园友多多指正,谢谢! 关于原文作者 原文作者:Shivprasad koirala 原文地址:http://www.codeproject.com/Articles/76153/Six-import

堆栈,堆,值类型,引用类型,装箱和拆箱

当你声明一个变量时内存中都发生了什么? 当你在一个.Net应用程序中声明一个变量时,首先要分配一些内存快到RAM,它包括三样东西,第一个是变量名,第二个是变量的数据类型,最后一个是变量的值. 这只是一个很简单的解释,根据变量的数据类型不同,有两种内存分配类型:堆栈内存和堆内存. 图 2 声明变量后的内存结构 堆栈(stack)和堆(heap) 为了帮助理解堆栈和堆,让我们了解下面的代码内部究竟发生了什么. public void Method1() { // Line 1 int i=4; //

值类型和引用类型的区别

数据项的类型定义了存储数据需要的内存大小,组成该类型的数据成员以及该类型能执行的函数.类型还决了对象在内存中的存储位置-栈或堆. 类型被分为:值类型和引用类型,这两种类型的对象在内存中的存储方式不同.值类型只需要一段单独的内存,用于存储实际的数据,引用类型需要两段内存:第一段存储实际的数据,它总是位于堆中,第二段是一个引用(即内存地址,不懂是不是就是直接寻址),指向数据在堆中的存放位置. 如果数据不是其他类型的成员,是独立的,比如方法代码块声明的临时变量int age ;如果是某一类型的成员,,

C# 值类型与引用类型的详解

值类型与引用类型分这几种情况: 1.内存分为堆和栈,值类型的数据存储在栈中,引用类型的数据存储在堆中. 2.int numb=10,代码中的10是值类型的数据,numb只是一个指向10的变量而已.其中10存储在栈中,变量numb也存储在栈中. 3.Person p=new Person(),代码中 new Person()所创建的对象存储在堆中,变量p存储在栈中. 4.综合第2点和第3点所有变量都存储在栈中,无论此变量存储的值是值类型和引用类型. 5.值类型包括枚举,结构以及像int,datet

值类型与引用类型总结

下面是我关于值类型与引用类型的一些思考,错误之处肯定有的,欢迎高人批评指正,帮助进步 C#中,值类型和引用类型是两个非常重要的概念,下面尽量详细的阐述,观点不一定完全正确,欢迎批评指正. 1.内存分为堆和栈,值类型的数据存储在栈中,引用类型的数据存储在堆中. 2.int numb=10,代码中的10是值类型的数据,numb只是一个指向10的变量而已.其中10存储在栈中,变量numb也存储在栈中. 3.Person p=new Person(),代码中 new Person()所创建的对象存储在堆