我已经学习 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
那么之后将为空。相反, 的值y
只是一个引用,并且该引用是按值传递的。这很令人困惑,因为“参考”一词在这两个术语中都有,但它们是不同的东西。
您可能想查看我关于 C# 参数传递的文章,以了解如果 Java确实具有按引用传递语义会发生什么,就像 C# 在(且仅当)您使用ref
关键字时所做的那样。
您可能还想查看与该主题相关的我的 Stack Overflow 答案的评论。
我认为不同之处在于,在 Java 中,所有对象的传递都是通过指针自动完成的,而在 C 中,实际上必须在这里和那里撒上小星号和 & 号。
概念性的,说的很对。如果我们是迂腐的(这是一件好事),我们甚至可以说在 Java 中根本不传递对象。传递的只是“指针”,在 Java 中称为引用。所有间接都是自动完成的。因此,当您在 C 中执行“objref->foo”而不是“objref.foo”并且由于使用指针而不能使用点时,在 Java 中您仍然可以使用点,因为它不知道任何其他内容无论如何访问成员。在 C 中,您可以传递指针(这里实际上称为指针),也可以传递对象本身,在这种情况下传递一个副本。在 C 中,您可以使用星号或“->”间接访问指针所引用的对象。在 Java 中,
如果我们再次迂腐(现在再次变得更重要),那么在 Java 和 C 中都没有“按引用传递”。我们在 Java 中传递的是对象的引用/指针,而在 C 中,我们要么传递对象的副本(很明显),要么再次传递指向对象的指针的副本。所以在这两种情况下——Java 和 C——我们传递的是对象的地址。不谈论在 Java 和 C 中复制的原始 Java 类型 - 即使您也可以在 C 中传递它们的地址,聚合类型(即结构)和 C 中的“原始类型”之间没有区别看待。
差异是微妙的。在汇编级别,在这两种情况下,对象的地址都被传递给函数。但是,在指针的情况下,参数是一个独立的指针,可用于修改目标对象 (*pch = 'x') 或可用于访问不同的对象 (pch = "newstr")。在参考情况下,参数会自动间接指向目标对象。
一些东西
我相信指针是一个包含引用的变量。使用 & 可以获得内存方向(参考),使用 * 可以获得内存方向的内容。我推荐这本书The C programming Language。
问候
真正的传递引用意味着您可以为引用分配一个新值,并且这个新值将在调用上下文中可见,就像在被调用方法内部一样。
在 Java 中,这是不可能的,因为对象引用是按值传递的。
如果您正在学习 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# 中,指针被标识为地址。
与引用相比,使用包含地址的指针可以做更多事情。地址可以用数学方法进行操作;您可以从另一个中减去一个,您可以向它们添加整数,等等。它们的合法操作表明它们是索引到作为进程虚拟地址空间的“数组”的“花哨数字”。
那些熟悉 C/C++ 的读者可能已经注意到对象引用看起来类似于指针。这种怀疑基本上是正确的。对象引用类似于内存指针。主要区别——也是 Java 安全的关键——是不能像操作实际指针那样操作引用。因此,您不能使对象引用指向任意内存位置或像整数一样操作它。
MyClass object1 = new MyClass();
我的类 object2 = object1;
即,您可能认为 object1 和 object2 指的是独立且不同的对象。然而,这是错误的。相反,在这个片段执行之后,object1 和 object2 都将引用同一个对象。如果你改变任何一种效果。
*初始化后不能更改对象的地址 ie MyClass obj =new MyClass(); 作为 c++ 中的参考,使用 object 您只能更改对象实例值
注意:java提供了一个特殊的特性object1已经被设置为null,但是object2仍然指向原来的对象。
我不确定 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++;
}
请注意,x 等于 1,但是当您调用方法 Foo 并在 Foo 中 x 增加 1 时通过引用传递 x 时,原始值也会增加