From:
学习加记录。
本部分主要讲Pointer基本概念。
Parameters, the Big Picture
当我们调用一个方法的时候,会发生如下情况:
- 为在Stack上执行的方法所需要的信息分配空间(called stack frame). 包括正在运行的函数的调用地址(calling address)。调用地址(call address)是一个指针,一般是一个GoTo的指令,起作用是当线程完成方法调用后能够知道返回到何处以便能够继续执行。
- 将要调用的方法的参数复制到Stack上。
- 控制权转移到了编译过的要调用的方法上,线程开始执行要调用的方法的代码,因此我们Call Stack上有了当前调用方法的stack frame。
代码如下:
public int AddFile(int pValue)
{
int result;
result = pValue + 5;
return result;
}
NOTE : the method does not live on the stack, and is illustrated here just for reference as the beginnnig of the stack frame.
关于放到Stack上的参数依据是值类型还是引用类型来处理,值类型是直接复制,引用类型则是复制相关的指针。
passing Value Type
当我们传递一个值类型的时候,在Stack上会分配出来一段相应的空间,我们的值类型会被复制到Stack上刚刚分配的空间上。看如下代码:
class Class1
{
public void Go(){
int x = 5;
AddFive(x);
Console.WriteLine(x.ToString())
}
public int AddFive(int pValue)
{
pValue += 5;
return pValue;
}
}
所以,很容易理解输出结果为什么是5了吧。关键点是任何传递给方法的值类型参数都是副本复制(carbon copy), 原始值被保留了下来。
另外一件需要注意的事情是:如果我们有很大的值类型数据,比如说比较大的Struct,当他被传递个Stack的时候,每次都会消耗很多的Cpu和内存空间在Stack上。当Stack没有足够多的空间的时候,就会溢出。Struct是一个可以很大的值类型数据,当我们处理他的时候需要小心。
这里有一个比较大的Struct:
public struct MyStruct
{
long a,b,c,d,e,f,g,h,i,j,k,l,m,n;
}
让我们看看当我们执行如下代码的时候会发生什么事情:
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)
{
// DO SOMETHING HERE....
}
这种方式能够使得我们的内存使用更加的有效率。
需要注意的是:当我们通过引用传递的时候,我们是传递的变量所在的地址,修改的也是变量所在的地址。