我已经学习了几个月的Java,现在开始学习C语言。
我有些困惑,我一直以为通过引用传递对象和通过指针传递对象是一样的:我认为它们的区别在于,在Java中所有对象的传递都是自动使用指针进行的,而在C语言中需要在某些地方添加星号和取地址符号。最近在谈话中,有人向我保证确实有区别!
通过引用传递和通过指针传递之间有什么区别?
我已经学习了几个月的Java,现在开始学习C语言。
我有些困惑,我一直以为通过引用传递对象和通过指针传递对象是一样的:我认为它们的区别在于,在Java中所有对象的传递都是自动使用指针进行的,而在C语言中需要在某些地方添加星号和取地址符号。最近在谈话中,有人向我保证确实有区别!
通过引用传递和通过指针传递之间有什么区别?
Java和C都不存在按引用传递的方式。它们严格采用按值传递的方法。
按引用传递的语义意味着,当您在方法中更改参数的值时,调用者将看到该参数的更改。
现在,您可能会想:“但在Java中是这样的!如果我在方法中改变一个对象,调用者会看到那个变化。”对象不是参数。参数只是变量-如果您更改该变量的值,则调用者不会看到那个变化。例如:
public void foo(Object x)
{
x = null;
}
public void caller()
{
Object y = new Object();
foo(y);
// y is still not null!
}
如果参数确实是按引用传递的,那么y
之后将为null。相反,y
的值只是一个引用,并且该引用以按值传递。这很令人困惑,因为“引用”一词在两个术语中都出现了,但它们是不同的东西。ref
关键字时才会出现。这两种方法的区别微妙。在汇编级别上,两种情况下对象的地址都会被传递给函数。但是,在指针情况下,参数是一个独立的指针,可以用于修改目标对象(*pch = 'x'),也可以用于访问不同的对象(pch = "newstr")。而在引用情况下,参数会自动间接地指向目标对象。
一些事情
真正的按引用传递意味着你可以给引用赋一个新值,这个新值将会在调用上下文中可见,就像在被调用的方法内部一样。
在Java中,这是不可能的,因为对象引用是按值传递的。
熟悉C/C++的读者可能已经注意到对象引用与指针相似。这种怀疑基本上是正确的。对象引用类似于内存指针。主要区别 - 也是Java安全性的关键 - 是您不能像操作实际指针一样操作引用。因此,您不能使对象引用指向任意内存位置或像整数一样操作它。
MyClass object1 = new MyClass();
MyClass object2 = object1;
即你可能认为object1和object2引用了不同的对象。然而,这是错误的。在此片段执行后,object1和object2将都引用同一个对象。如果您更改其中任何一个,则会影响另一个。
*您无法在初始化后更改对象的地址 即 MyClass obj =new MyClass(); 作为C++中的引用,您只能更改对象实例值
注意:Java提供了一个特殊功能 object1已设置为null,但object2仍指向原始对象。
如果你正在学习C语言(而不是C++),那么就没有引用。
C语言中的“按引用传递”这个术语仅意味着您将指针作为存储值的地址进行传递,以便函数可以更改其值。否则,您将按值传递,这意味着在堆栈上生成变量的副本,并且修改没有影响。
在许多方面,这与Java中所看到的类似,只是在Java中,您不必显式地将事物转换为指针或取消引用它们。换句话说,当您传递指针时,地址会在堆栈上复制,因此,如果您的函数更改指针(而不是它指向的数据),则更改会在完成函数后消失。同样,在Java中传递对象引用时,您可以更改对象的内容(例如通过调用函数),但是一旦离开,将变量指向另一个对象将不起作用。
如果您使用的是类似于C的C ++,则可以按引用传递,在这种情况下,您无需处理指针,并且函数中对变量的更改实际上直接更改外部值(除了您不能使其指向其他东西)。
最近Eric Lippert发表了一篇关于这个问题的文章。他是C#编译器的程序员,但他在那里说的话具有足够的普遍性,可以扩展到其他GC语言,如Java:
http://blogs.msdn.com/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx
文章引用:
指针比引用严格来说“更强大”;你可以用指针做任何引用能做的事情。指针通常被实现为地址。地址是一个数字,它是进程整个虚拟地址空间中“字节数组”的偏移量。出于所有这些原因,我们在规范中不将引用描述为地址。规范只是说引用类型的变量“存储对对象的引用”,并完全模糊地说明了如何实现它。同样,指针变量存储“地址”,这也是相当模糊的。我们从未说过引用和地址是相同的。因此,在C#中,引用是一种让你引用对象的模糊东西。你不能对引用做任何事情,除了解引用它,并将其与另一个引用进行比较以检查是否相等。而在C#中,指针被识别为地址。与引用相比,指针包含地址,你可以做更多的事情。地址可以进行数学操作;你可以从一个地址中减去另一个地址,你可以将整数加到地址上等等。它们的合法操作表明它们是索引进程虚拟地址空间的“数组”的“高级数字”。我不确定Java,但在C#或VB.net中,您可以按值传递或按引用传递。
例如,通过引用传递参数:
static void Main(string[] args)
{
int x = 1;
Foo(ref x);
Console.WriteLine(x.ToString());//this will print 2
}
private static void Foo(ref int x)
{
x++;
}
foo(&y)
的情况,传递的是参数的值。那么参数是什么?是&y
。该表达式获取了y
的地址,然后将结果按值传递。C++确实有按引用传递的方式,但C语言并没有。 - Jon Skeet