15

我已经学习 Java 几个月了,现在开始学习 C。

我有点困惑,我的印象是通过引用传递对象和传递指向该对象的指针是一回事:我认为不同之处在于,在 Java 中,所有对象的传递都是使用指针自动完成的,如C 一个人实际上必须在这里和那里撒上小星号和&符号。最近,在谈话中,我确信这是有区别的!

通过引用传递和传递指针有什么区别?

4

10 回答 10

39

JavaC 都没有传递引用。它们都是严格按值传递的。

引用传递语义意味着当您更改方法中参数的值时,调用者将看到参数中的更改。

现在,您可能会想:“但在 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 答案的评论。

于 2009-03-01T22:30:20.283 回答
7

我认为不同之处在于,在 Java 中,所有对象的传递都是通过指针自动完成的,而在 C 中,实际上必须在这里和那里撒上小星号和 & 号。

概念性的,说的很对。如果我们是迂腐的(这是一件好事),我们甚至可以说在 Java 中根本不传递对象。传递的只是“指针”,在 Java 中称为引用。所有间接都是自动完成的。因此,当您在 C 中执行“objref->foo”而不是“objref.foo”并且由于使用指针而不能使用点时,在 Java 中您仍然可以使用点,因为它不知道任何其他内容无论如何访问成员。在 C 中,您可以传递指针(这里实际上称为指针)也可以传递对象本身,在这种情况下传递一个副本。在 C 中,您可以使用星号或“->”间接访问指针所引用的对象。在 Java 中,

如果我们再次迂腐(现在再次变得更重要),那么在 Java 和 C 中都没有“按引用传递”。我们在 Java 中传递的是对象的引用/指针,而在 C 中,我们要么传递对象的副本(很明显),要么再次传递指向对象的指针的副本。所以在这两种情况下——Java 和 C——我们传递的是对象的地址。不谈论在 Java 和 C 中复制的原始 Java 类型 - 即使您也可以在 C 中传递它们的地址,聚合类型(即结构)和 C 中的“原始类型”之间没有区别看待。

于 2009-03-01T22:41:53.077 回答
3

差异是微妙的。在汇编级别,在这两种情况下,对象的地址都被传递给函数。但是,在指针的情况下,参数是一个独立的指针,可用于修改目标对象 (*pch = 'x') 或可用于访问不同的对象 (pch = "newstr")。在参考情况下,参数会自动间接指向目标对象。

于 2009-03-01T22:23:23.187 回答
2

一些东西

  • C 没有引用(尽管有 C++)
  • Java没有指针
  • 指针和引用之间的区别在于,指针实际上是您可以计算的内存地址。无法计算参考
  • Java和C按值传递(Java中传递对象时,将引用复制到函数中)
于 2009-03-01T22:25:09.780 回答
2

我相信指针是一个包含引用的变量。使用 & 可以获得内存方向(参考),使用 * 可以获得内存方向的内容。我推荐这本书The C programming Language

问候

于 2009-03-01T22:25:33.523 回答
2

真正的传递引用意味着您可以为引用分配一个新值,并且这个新值将在调用上下文中可见,就像在被调用方法内部一样。

在 Java 中,这是不可能的,因为对象引用是按值传递的

于 2009-03-01T22:26:21.837 回答
1

如果您正在学习 C(而不是 C++),则没有参考资料。

C 中的术语“按引用传递”仅表示您将指针作为存储值的地址传递,以便函数可以更改其值。否则,您将按值传递,这意味着在堆栈上生成变量的副本并且修改没有影响。

在许多方面,这与您在 Java 中看到的相似,只是在 Java 中您不必显式地将事物转换为指针或取消引用它们。换句话说,当你传递一个指针时,地址被复制到堆栈上,所以如果你的函数改变了指针(而不是它指向的数据),当你完成函数时,更改就会消失。类似地,当您在Java 中传递对象引用时,您可以更改对象的内容(例如,通过调用函数),但是一旦您离开,将变量更改为指向另一个对象将不起作用。

如果您使用的是 C++(看起来像 C),那么您可以通过引用传递,在这种情况下您不需要处理指针,并且函数中变量的更改实际上会直接更改外部值(除了您不能让它指向别的东西)。

于 2009-03-01T22:23:55.023 回答
1

Eric Lippert 最近有一篇关于此的文章。他是 C# 编译器的程序员,但无论他说什么,都具有足够的通用性,可以扩展到其他 GC 语言,例如 Java:

http://blogs.msdn.com/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx

引用文章:

严格来说,指针比引用“更强大”;你可以用引用做任何事情,你可以用指针做任何事情。[...]

指针通常实现为地址。地址是一个数字,它是“字节数组”的偏移量,“字节数组”是进程的整个虚拟地址空间。[...]

由于所有这些原因,我们没有将引用描述为规范中的地址。规范只是说引用类型的变量“存储了对对象的引用”,并且对于如何实现它完全模糊。类似地,指针变量存储对象的“地址”,这又是非常模糊的。我们没有说引用与地址相同。

因此,在 C# 中,引用是一些模糊的东西,可以让您引用一个对象。除了取消引用之外,您不能对引用做任何事情,并将其与另一个引用进行比较以获得相等性。在 C# 中,指针被标识为地址。

与引用相比,使用包含地址的指针可以做更多事情。地址可以用数学方法进行操作;您可以从另一个中减去一个,您可以向它们添加整数,等等。它们的合法操作表明它们是索引到作为进程虚拟地址空间的“数组”的“花哨数字”。

于 2009-03-01T22:28:29.647 回答
1

那些熟悉 C/C++ 的读者可能已经注意到对象引用看起来类似于指针。这种怀疑基本上是正确的。对象引用类似于内存指针。主要区别——也是 Java 安全的关键——是不能像操作实际指针那样操作引用。因此,您不能使对象引用指向任意内存位置或像整数一样操作它。

  • java 不支持 & 运算符,那么如何在 java 中获取对象地址。
  • 在 java 中,引用是由对象完成的,例如

MyClass object1 = new MyClass();

我的类 object2 = object1;

即,您可能认为 object1 和 object2 指的是独立且不同的对象。然而,这是错误的。相反,在这个片段执行之后,object1 和 object2 都将引用同一个对象。如果你改变任何一种效果。

*初始化后不能更改对象的地址 ie MyClass obj =new MyClass(); 作为 c++ 中的参考,使用 object 您只能更改对象实例值

注意:java提供了一个特殊的特性object1已经被设置为null,但是object2仍然指向原来的对象。

于 2011-07-08T18:23:29.027 回答
0

我不确定 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 时,原始值也会增加

于 2009-03-01T22:52:02.070 回答