Chinaunix首页 | 论坛 | 博客
  • 博客访问: 232271
  • 博文数量: 75
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 848
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-08 10:27
文章分类
文章存档

2014年(9)

2013年(66)

我的朋友

分类: C#/.net

2013-11-30 19:19:31

简介


这篇文章我们将介绍一些方法参数传递行为在堆与栈中的影响。前几节我们介绍了堆与栈的基本工作原理,程序执行时值类型与引用类型在堆栈中的存储。另外,我们已经介绍了一些关于指针的基本知识。这一节中参数传递对堆栈的影响很重要,下面会慢慢道来。


参数,大画面
下面是当代码运行时会产生的一个详细过程。上几节已经介绍过当一个方法被调用时会产生的基本情况,让我们来看一下更加详细的内容。
当我们调用一个方法时会发生以下情形:
栈会分配一块内存空间给程序执行所需要的信息(我们叫它栈结构Stack Frame)。一个栈结构包含方法调用地址(指针),它以一个GOTO指令的形式存在栈里。因此,当线程序执行完方法(method),它会知道怎么样返回进而曳继续执行代码。
方法的所有参数将被复制到栈里,这是我们将要更加详细介绍的部分。
控制被传递到JIT编译过的方法里,同时线程开始执行代码。此时,我们将有另一个方法呈现在栈结构的“回调栈”里。
代码:
[csharp] view plaincopy
public int AddFive(int pValue)  
          {  
                int result;  
                result = pValue + 5;  
                return result;  
          }  
像前几节介绍的,值类型和引用类型在栈里的存储是不同的。栈为任何值类型创建副本,栈也为任何引用类型的指针创建副本。


值类型传递
下面是值类型传递在栈里的内幕。


首先,当我们传递一个值类型变量时,栈会为它分配一块内存空间并把值类型变量的值存储进去。看下面的代码:
[csharp] view plaincopy
class Class1  
     {  
          public void Go()  
          {  
              int x = 5;  
              AddFive(x);  
   
              Console.WriteLine(x.ToString());  
                
          }  
   
          public int AddFive(int pValue)  
          {  
              pValue += 5;  
              return pValue;  
          }  
     }  


当代码执行时,栈为x分配一块内存空间并存储值5


然后,AddFive()被放到栈上,同时栈分配内存空间给参数pValue并复制x的值给它。


当AddFive()执行完成,线程被传递回Go()。同时因为AddFive()执行完,它的参数pValue也实质上被移除。


所以结果是5是合理的。关键点是任何被传递的值类型参数仅是一个碳复制,因为我们希望保护原始变量的值。
有一点要记住的是,如果我们有一个非常庞大的值类型(如,庞大的struct类型)传递到栈里,当处理器循环复制它并循环占有栈空间时将会非常耗资源。栈没有无限的空间去使用,就像用水杯不断的接水早晚会溢出一样。Struct类型可以变得非常庞大,我们要小心并清醒的使用它。


下面是一个比较大的struct结构类型:
[csharp] view plaincopy
public struct MyStruct  
          {  
              long a, b, c, d, e, f, g, h, i, j, k, l, m;  
          }  


让我们看看执行下面代码Go()方法时再到DoSomething()方法会发生的情况:
[csharp] view plaincopy
public void Go()  
         {  
            MyStruct x = new MyStruct();  
            DoSomething(x);  
               
         }  
  
  
          public void DoSomething(MyStruct pValue)  
          {  
                   // 省略具体实现....  
          }  




这可能会非常低效。想像一下如果我们传递MyStruct几千次,它会怎么样让程序死掉。


那么,我们怎么才能回避这样的问题呢?那就是仅传递原始值类型的引用。
public void Go()
          {
             MyStruct x = new MyStruct();
             DoSomething(ref x);
              
          }
 
           public struct MyStruct
           {
               long a, b, c, d, e, f, g, h, i, j, k, l, m;
           }
 
           public void DoSomething(ref MyStruct pValue)
           {
                    // 省略实现....
           }




这样就能节省内存并提升内存使用效率




唯一需要注意的是传递引用时我们在访问原始变量x的值,任可对pValue的改变都会影响到x。
下面的代码会将x改变成"12345",因为pValue.a实际上指向原始x声明时所在的内存地址。
[csharp] view plaincopy
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;  
          }  

阅读(1165) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~