【发布时间】:2010-10-10 16:22:08
【问题描述】:
我已经学习 Java 几个月了,现在开始学习 C。
我有点困惑,我的印象是通过引用传递对象和传递指向该对象的指针是一回事:我认为不同之处在于,在 Java 中,所有对象的传递都是通过指针自动完成的,就像在 C 中一样,实际上必须在这里和那里撒上小星号和 & 符号。最近,在谈话中,我确信这是有区别的!
引用传递和指针传递有什么区别?
【问题讨论】:
标签: java c pointers pass-by-reference
我已经学习 Java 几个月了,现在开始学习 C。
我有点困惑,我的印象是通过引用传递对象和传递指向该对象的指针是一回事:我认为不同之处在于,在 Java 中,所有对象的传递都是通过指针自动完成的,就像在 C 中一样,实际上必须在这里和那里撒上小星号和 & 符号。最近,在谈话中,我确信这是有区别的!
引用传递和指针传递有什么区别?
【问题讨论】:
标签: java c pointers pass-by-reference
熟悉 C/C++ 的读者可能已经注意到对象引用的出现 类似于指针。这种怀疑基本上是正确的。对象引用是 类似于内存指针。主要区别——也是 Java 安全的关键——是 您不能像操作实际指针一样操作引用。因此,您不能导致 指向任意内存位置或像整数一样操作它的对象引用。
MyClass object1 = new MyClass();
MyClass object2 = object1;
即您可能会认为 object1 和 object2 指的是独立且不同的对象。 然而,这是错误的。相反,在这个片段执行之后,object1 和 object2 都将引用同一个对象。如果你改变任何一种效果。
*初始化后不能更改对象的地址 即 MyClass obj =new MyClass(); 作为 c++ 中的参考,使用对象只能更改对象实例值
注意: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 时,原始值也会增加
【讨论】:
我认为不同之处在于,在 Java 中,所有对象的传递都是通过指针自动完成的,而在 C 中,实际上必须在各处添加小星号和与号。
概念,这是完全正确的。如果我们是迂腐的(这是一件好事),我们甚至可以说在 Java 中根本不传递对象。传递的只是“指针”,在 Java 中称为引用。所有间接都是自动完成的。因此,当您在 C 中执行“objref->foo”而不是“objref.foo”并且由于使用指针而不能使用点时,在 Java 中您仍然可以使用点,因为它不知道任何其他内容无论如何访问成员。在 C 中,您可以传递指针(这里,它是实际上称为指针)并且您可以传递对象本身,在这种情况下传递一个副本。在 C 中,您可以使用星号或“->”间接访问指针所引用的对象。在 Java 中,访问对象的唯一方法是使用点 (objref.member)。
如果我们又学究气了(现在又变得更重要了),那么在 Java 和 C 中都没有“通过引用传递”。我们在 Java 中传递的是对象的引用/指针,而在 C 中,我们要么传递对象的副本(很明显),要么再次传递指向对象的指针的副本。所以在这两种情况下——Java 和 C——我们传递的是对象的地址。不谈论在 Java 和 C 中复制的原始 java 类型 - 即使您也可以在 C 中传递它们的地址,聚合类型(即结构)和 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 的值只是一个引用,并且该引用是按值传递的。这很令人困惑,因为“参考”一词在这两个术语中都有,但它们是不同的东西。
您可能想查看my article about C# parameter passing 以了解如果Java 确实 具有传递引用语义会发生什么,就像C# 在(且仅当)您使用@987654326 时所做的那样@关键字。
您可能还想查看与该主题相关的 cmets 至 my Stack Overflow answer。
【讨论】:
foo(&y) 的情况下,参数的值被传递。论据是什么? &y。该表达式采用y 的地址,然后按值传递结果。 C++ 确实有传递引用,但 C 真的没有。
Eric Lippert 最近有一篇关于此的文章。他是 C# 编译器的程序员,但无论他说什么,都具有足够的通用性,可以扩展到其他 GC 语言,例如 Java:
http://blogs.msdn.com/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx
引用文章:
指针严格来说比引用“更强大”;你可以用引用做任何事情,你可以用指针做任何事情。 [...]
指针通常实现为地址。地址是一个数字,它是“字节数组”的偏移量,“字节数组”是进程的整个虚拟地址空间。 [...]
出于所有这些原因,我们在规范中没有将引用描述为地址。规范只是说引用类型的变量“存储了对对象的引用”,并且对于如何实现它完全模糊。类似地,指针变量存储对象的“地址”,这又是非常模糊的。我们没有说引用与地址相同。
因此,在 C# 中,引用是一些模糊的东西,可以让您引用一个对象。除了取消引用之外,您不能对引用做任何事情,并将其与另一个引用进行比较以获得相等性。而在 C# 中,指针被标识为地址。
与引用相比,使用包含地址的指针可以做更多事情。地址可以用数学方法进行操作;您可以从另一个中减去一个,您可以向它们添加整数,等等。它们的合法操作表明它们是“花哨的数字”,索引到作为进程虚拟地址空间的“数组”中。
【讨论】:
True pass-by-reference 意味着您可以为引用分配一个新值,并且这个新值将在调用上下文中可见,就像在被调用方法内部一样。
在 Java 中,这是不可能的,因为object references are passed by value。
【讨论】:
我相信指针是一个包含引用的变量。 使用 & 可以获得内存方向(参考),使用 * 可以获得内存方向的内容。 我推荐这本书The C programming Language。
问候
【讨论】:
如果您正在学习 C(而不是 C++),则没有参考资料。
C 中的术语“按引用传递”仅表示您将指针作为存储值的地址传递,以便函数可以更改其值。 否则,您将按值传递,这意味着在堆栈上生成变量的副本并且修改没有影响。
在许多方面,这与您在 Java 中看到的相似,只是在 Java 中您不必显式地将事物转换为指针或取消引用它们。换句话说,当你传递一个指针时,地址被复制到堆栈上,所以如果你的函数改变了指针(而不是它指向的数据),当你完成函数时,改变就会消失。同样,当您在 Java 中传递对象引用时,您可以更改对象的内容(例如,通过调用函数),但是一旦您离开,将变量更改为指向另一个对象将没有任何效果。
如果你使用的是C++(看起来像C),那么你可以通过引用传递,在这种情况下你不需要处理指针,而函数中变量的改变实际上直接改变了外部值(除了你不能让它指向别的东西)。
【讨论】:
差异是微妙的。在汇编级别,在这两种情况下,对象的地址都被传递给函数。但是,在指针的情况下,参数是一个独立的指针,可用于修改目标对象 (*pch = 'x') 或可用于访问不同的对象 (pch = "newstr")。在引用情况下,参数会自动间接到目标对象。
【讨论】: