C# 函数参数传递(按值和引用)

C#中的数据类型.分值类型和引用类型两大类.

  值类型:直接存储数据的值,保存在内存中的stack(堆栈)中

  引用类型:存储对值的引用,实际上存储的就是一个内存的地址.引用类型的保存分成两块,实际值保存在托管堆(heap)中.实际值的内存地址保存在stack中

  当使用引用类型时先找到stack中的地址,再找到heap中的实际值.

  也就是说保存引用类型时要用到stack和heap,但使用引用类型时我们实际上只用到stack中的值,然后通过这个值间接的访问heap中的值

  C#预定义的简单类型,像int,float,bool,char都是值类型,另外enum(枚举),struct(结构)也是值类型

  string,数组,自定义的class就都是引用类型了.其中的string是比较特殊的引用类型.C#给它增加个字符恒定的特性.

  C#函数的参数如果不加ref,out这样的修饰符显式申明参数是通过引用传递外,默认都是值传递.

  这里要注意的一个问题是,参数的类型是值类型还是引用类型和传参数时用值传递还是引用传递是两个不同的概念.

  假如有void FunTest(int [] array) 和void FunTest(int a) 
这两个函数.参数array是引用类型,a是值类型.但是他们传递时都是按值传递.

  我们来举个例子说明下

  按值传递参数:

  class Program

  {

  public static void ChangeInt(int num)

  {

  num = 123;

  }

  public static void ChangeArray(int[] array)

  {

  array[0] = 10;

  array = new int[] { 6, 7, 8, 9 };

  }

  static void Main(string[] args)

  {

  int anum = 1;

  int[] aarray = { 1, 2, 3 };

  ChangeInt(anum);

  ChangeArray(aarray);

  Console.WriteLine("value of num: " + anum);

  Console.Write("value of aarray: ");

  foreach (int i in aarray)

  Console.Write(i + " ");

  }

  }

  结果是:value of anum : 1

  value of aarray :10 2 3

  可能看到结果会有点奇怪.我们一般认为值传递就是把值拷贝一份,然后不管在函数中对传入的参数做啥改变,参数之前的值不会受啥影响,所以anum没有变成123,仍然是1

  但是aarray[0]为啥却变成10了呢?

  前面我们有说到引用类型在内存中是保存为两个部分,一个是stack中的内存地址,另一个是heap中的实际值.用时我们只直接用stack中的值,我们假如stack中的值为0xabcdefgh 
,就说是aaraay指向它吧. 那么我们按值传递时就是把这个stack的值拷贝成另一份就假如是array指向它吧.跟拷贝anum的值1一样.

  但是我们操作内存地址这样的值时不会像整数一样直接操作它,而只会通过它去找heap中的实际值.

  于是我们array[0] = 10.改变了实际上还是heap中数组的值了. 但array = new int [] 
{6,7,8,9}没有对之前传的aarray产生影响.这个操作的意义是在heap中重新开辟一块内存,保存着值6,7,8,9. 
这这块内存的地址赋给array,于是它之前的值0xabcdefgh被改写了.但aarray指的值stack值仍没变,仍是0xabcdefgh

  按引用传递参数

  可以用out或ref显式指定.它们大部分时候可以通用,只是有一点细小区别.

  先用ref 来举例吧,还用上面的例子,只是加个了关键字ref

  class Program

  {

  public static void ChangeInt(ref int num)

  {

  num = 123;

  }

  public static void ChangeArray(ref int[] array)

  {

  array[0] = 10;

  array = new int[] { 6, 7, 8, 9 };

  }

  static void Main(string[] args)

  {

  int anum = 1;

  int[] aarray = { 1, 2, 3 };

  ChangeInt(ref anum);

  ChangeArray(ref aarray);

  Console.WriteLine("value of num: " + anum);

  Console.Write("value of aarray: ");

  foreach (int i in aarray)

  Console.Write(i + " ");

  }

  }

  结果是:value of anum : 123

  value of aarray :6 7 8 9

  跟按值传递的结果完全不同吧

  num = 123我们是容易理解.我们再来说下aarray的值为啥变了吧

  按引用传递时aarray指向的stack中的值不会复制一份,而是直接传过去.这样array[0]= 10这样赋值时也同样改变了heap中 1 2 3 
的值,变为10 2 3,如果

  没有array = new int [] {6,7,8,9} 
这个语句,则它的结果跟上面按值传递是完全一样的.但有个这句话后就不一样,我们知道上面说了它的含义,在heap中开辟一块新内存

  值是6 7 8 9,而aarray指向的stack的值被改写了,改为指向保存6 7 8 9的内存地址了.那含有10 2 
3的那一块内存其实还继续存在,只是没有谁引用到它了.到时垃圾回收器会把它回收的.

  补充:

  说下out 和ref的细小区别

  ref 传进来的参数必须要先赋值.

  像上面 的例子中如果这样写

  int num;

  ChangeInt(ref int num);

  就会出错,必须先给num给个值1.

  而且out传进来的参数可以不先赋值.

  out num;

  ChangeInt(out int num);是对的

  另外还有个区别就是如果用out的时候ChangeInt函数中必须有某个地方给num赋值了,而用ref不一定需要在函数中给num赋值

  其实这样做的目的很好理解.C#为了确保在任何情况下num必须有个值,不能为空.

  因为用ref,在调用函数前必须保证参数有值,所以在函数中就不必要求它一定再赋值

  而用out由于在调用函数前不用保证参数必须有值,所以在函数中必须保证给它个值

  ChangeInt(ref int num)和ChangeInt(out int num)虽然不一样,但是不同共存,不能当作两个不同的函数

  而ChangeInt(int num)和上面 的两个函数是完全不一样的,可以放到一起共存

  这样的话调用的时候ref ,out这样的关键字不能省的.必须匹配

时间: 2024-10-12 11:45:02

C# 函数参数传递(按值和引用)的相关文章

【转载】函数参数传递(值传递,指针传递)

C语言值传递 地址传递 : 1.值传递 eg: 1     void Exchg1(int x, int y)   2     {   3          int tmp;   4          tmp = x;   5          x = y;   6          y = tmp;   7          printf("x = %d, y = %d\n", x, y);   8     }   9     main()  10     {  11        

c++中函数参数传递(值传递、指针传递,引用传递)进一步认识

概念 首先从概念上来说一下这几种函数传参方式及区别: 1.值传递:形参是实参的拷贝,改变函数形参的值并不会影响外部实参的值,这是最常用的一种传参方法,也是最简单的一种传参方法,只需要传递参数,返回值那是return考虑的: 2.指针传递:指针传递参数从本质上来说也是值传递,它传递的是一个地址.[值传递过程中,被调函数的形参作为被调函数的局部变量来处理,即在函数内的栈中开辟内存空间以存放由主调函数放进来的实参的值,从而成了实参的一个副本(记住这个,函数内参数的是实参的副本)].由于指针传递的是外部

javascript函数参数传递是值传递还是引用传递?

网上对于js的函数参数传递方式众说纷纭,这里也是自己在网上挑选了比较好的解释: js和java是比较相像的,懂得java中的参数传递方式理解js就不难. 如果说一个变量的值是绝对的基本类型变量如: function add(num){ num+=10; return num; } num=10; alert(add(num)); aelrt(num); //输出20,10 这种结果应该大家都没有任何意义,基本类型变量的值本身就是不可改变的,变的是参数地址.就算变量a和b的值都是10但是二者并没有

为什么类的拷贝构造参数加引用、重载赋值函数的返回值和参数加引用

class string { public: string(const char *str=NULL); string(const string& str);     //copy构造函数的参数为什么是引用呢? string& operator=(const string & str); //赋值函数为什么返回值是引用呢?参数为什么是引用呢? ~string(); }; 下面我就给大家解释一下: class String1 { public: String1(const char*

php函数间的参数传递(值传递/引用传递)

php:函数间的参数传递 1.值传递 代码如下: <?php function exam($var1){ $var1++: echo "In Exam:" . $var1 . "<br />"; } $var1 = 1; echo $var1 . "<br />"; exam($var1); echo $var1 . "<br />"; ?> ------------------

JavaScript 函数参数传递到底是值传递还是引用传递

tips:这篇文章是听了四脚猫的js课程后查的,深入的理解可以参看两篇博客: JavaScript数据类型--值类型和引用类型 JavaScript数据操作--原始值和引用值的操作本质 在传统的观念里,都认为JavaScript函数传递的是引用传递(也称之为指针传递),也有人认为是值传递和引用传递都具备.那么JS的参数传递到底是怎么回事呢?事实上以下的演示也完全可以用于Java 首先来一个比较简单的,基本类型的传递: function add(num){ num+=10; return num;

python的参数传递是值传递还是引用传递??

函数参数传递机制,传值和引用的是什么意思? 函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题.基本的参数传递机制有两种:值传递和引用传递. 值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间来存放由主调函数放进来的实参的值,从而想成为了实参的一个副本.值传递的特点是被调函数对形势参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值. 引用传递(pass-by-refer

C++11函数返回右值引用

我们定义了一个可以传入右值引用的构造函数的类B,在使用std::move的时候,我们非常容易犯一个错误.看下面的代码: class B { public: B() :s(10), ptr(new int[s]) { std::cout << "default constructor" << std::endl; for (int i = 0; i < s; i++) { ptr[i] = i; } } B(const B& b) :s(b.s) {

C++函数参数-值,指针,引用

本文转载自SurpassLi的博文:http://www.cnblogs.com/lidabo/archive/2012/05/30/2525837.html,修改了部分代码和贴图,如有侵犯版权请与我联系删除.           以“ 值传递”方式向函数传递参数 在编写个人函数的时候,你将会受到C++中的一条基本的原则的限制:在默认的情况下,变量只能以值传递的方式传递给函数.这句话的意思是:被传递到函数的只是变量的值,永远不是变量的本身. 例如: void changeValue(int or