通过引用传递或通过值传递?

在学习一种新的编程语言时,您可能遇到的一个可能的障碍就是该语言默认情况下是按值传递还是按引用传递的问题。

所以这是我的问题,以你最喜欢的语言向你们所有人介绍,它是如何实际完成的? 有什么可能的陷阱?

当然,你最喜欢的语言可以是你曾经玩过的任何东西:流行的,晦涩的,深奥的,新的,旧的......


这是我对Java编程语言的贡献。

首先是一些代码:

public void swap(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}

调用这个方法会导致这样的结果:

int pi = 3;
int everything = 42;

swap(pi, everything);

System.out.println("pi: " + pi);
System.out.println("everything: " + everything);

"Output:
pi: 3
everything: 42"

即使使用“真实”对象也会显示类似的结果:

public class MyObj {
    private String msg;
    private int number;

    //getters and setters
    public String getMsg() {
        return this.msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public int getNumber() {
        return this.number;
    }


    public void setNumber(int number) {
        this.number = number;
    }

    //constructor
    public MyObj(String msg, int number) {
        setMsg(msg);
        setNumber(number);
    }
}

public static void swap(MyObj x, MyObj y)
{
    MyObj tmp = x;
    x = y;
    y = tmp;
}

public static void main(String args[]) {
    MyObj x = new MyObj("Hello world", 1);
    MyObj y = new MyObj("Goodbye Cruel World", -1); 

    swap(x, y);

    System.out.println(x.getMsg() + " -- "+  x.getNumber());
    System.out.println(y.getMsg() + " -- "+  y.getNumber());
}


"Output:
Hello world -- 1
Goodbye Cruel World -- -1"

因此很显然,Java通过传递它的参数,因为pi的值和所有东西以及MyObj对象都没有交换。 请注意,“按值”是java中将参数传递给方法的唯一方法。 (例如像c ++这样的语言允许开发者在参数的类型之后使用' '引用参数)

现在是棘手的部分 ,或者至少是会混淆大部分新Java开发人员的部分:(从javaworld中借用的)
原作者:Tony Sintes

public void tricky(Point arg1, Point arg2)
{
    arg1.x = 100;
    arg1.y = 100;
    Point temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
public static void main(String [] args)
{
    Point pnt1 = new Point(0,0);
    Point pnt2 = new Point(0,0);
    System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
    System.out.println(" ");
    tricky(pnt1,pnt2);
    System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}


"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"

棘手的成功改变pnt1的价值! 这意味着对象通过引用传递,事实并非如此! 正确的语句是: 对象引用是按值传递的。

更多来自Tony Sintes:

该方法成功地改变了pnt1的值,尽管它是按值传递的; 但是,pnt1和pnt2的交换失败! 这是混乱的主要来源。 在main()方法中,pnt1和pnt2只不过是对象引用。 当你将pnt1和pnt2传递给tricky()方法时,Java就像任何其他参数一样通过值传递引用。 这意味着传递给该方法的引用实际上是原始引用的副本。 下面的图1显示了在Java将一个对象传递给一个方法后,两个引用指向同一个对象。

图1 http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass2b.gif

结论或长话短说:

  • Java 按值传递它的参数
  • “通过值”是java中将参数传递给方法的唯一方法
  • 使用从作为参数给定的对象中的方法将会在引用指向原始对象时改变对象。 (如果该方法本身改变某些值)
  • 有用的链接:

  • http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html
  • http://www.ibm.com/developerworks/java/library/j-passbyval/
  • http://www.ibm.com/developerworks/library/j-praxis/pr1.html
  • http://javadude.com/articles/passbyvalue.htm

  • 这里是另一篇关于c#编程语言的文章

    c#通过传递它的参数(默认)

    private void swap(string a, string b) {
      string tmp = a;
      a = b;
      b = tmp;
    }
    

    调用此版本的交换将因此没有结果:

    string x = "foo";
    string y = "bar";
    swap(x, y);
    
    "output: 
    x: foo
    y: bar"
    

    然而,与java c#不同的是,开发人员有机会通过引用传递参数,这是通过在参数类型之前使用'ref'关键字完成的:

    private void swap(ref string a, ref string b) {
      string tmp = a;
      a = b;
      b = tmp;
    } 
    

    这个交换将改变引用参数的值:

    string x = "foo";
    string y = "bar";
    swap(x, y);
    
    "output: 
    x: bar
    y: foo"
    

    c#也有一个out关键字 ,ref和out之间的区别是微妙的。 来自msdn:

    out参数的方法的调用者在调用之前不需要分配给作为out参数传递的变量; 但是,被调用者在返回之前需要分配给out参数。

    相反, 参考参数被认为是由被调用者初始分配的。 因此,被调用者在使用前不需要分配给ref参数。 参数参数传入和传出方法。

    像java一样,一个小缺陷就是依靠值传递的对象仍然可以使用它们的内部方法进行更改

    结论:

  • 默认情况下,c#传递参数的值
  • 但是当需要的参数也可以通过引用使用ref关键字来传递
  • 来自通过值传递的参数的内部方法将改变对象(如果该方法本身改变某些值)
  • 有用的链接:

  • http://msdn.microsoft.com/en-us/vcsharp/aa336814.aspx
  • http://www.c-sharpcorner.com/UploadFile/saragana/Willswapwork11162005012542AM/Willswapwork.aspx
  • http://en.csharp-online.net/Value_vs_Reference

  • Python使用按值传递,但由于所有这些值都是对象引用,所以净效果与通过引用类似。 但是,Python程序员更多地考虑对象类型是可变的还是不可变的。 可变对象可以在原地进行更改(例如,字典,列表,用户定义的对象),而不可变对象不能(例如整数,字符串,元组)。

    以下示例显示了一个传递两个参数,一个不可变字符串和一个可变列表的函数。

    >>> def do_something(a, b):
    ...     a = "Red"
    ...     b.append("Blue")
    ... 
    >>> a = "Yellow"
    >>> b = ["Black", "Burgundy"]
    >>> do_something(a, b)
    >>> print a, b
    Yellow ['Black', 'Burgundy', 'Blue']
    

    该行a = "Red"只是创建一个本地名称, a ,对于字符串值"Red"和对传入的参数没有影响(现在隐藏,作为a必须是指从以本地名称再打开) 。 无论参数是可变的还是不可变的,赋值都不是就地操作。

    b参数是对可变列表对象的引用, .append()方法执行列表的原地扩展,并添加新的"Blue"字符串值。

    (因为字符串对象是不可变的,所以它们没有任何支持就地修改的方法。)

    一旦函数返回,重新分配a已经没有效果,同时延长b清楚地表明传址引用样式调用的语义。

    如前所述,即使a的参数是一个可变类型,函数内的重新赋值也不是就地操作,因此不会改变传递的参数的值:

    >>> a = ["Purple", "Violet"]
    >>> do_something(a, b)
    >>> print a, b
    ['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']
    

    如果你不想让被调用的函数修改你的列表,你应该使用不可变的元组类型(由字面形式中的圆括号标识,而不是方括号),它不支持就地.append()方法:

    >>> a = "Yellow"
    >>> b = ("Black", "Burgundy")
    >>> do_something(a, b)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in do_something
    AttributeError: 'tuple' object has no attribute 'append'
    
    链接地址: http://www.djcxy.com/p/457.html

    上一篇: Pass by reference or pass by value?

    下一篇: What's the difference between passing by reference vs. passing by value?