在Java中按引用传递和在C中传递指针有什么区别?

15

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

我有些困惑,我一直以为通过引用传递对象和通过指针传递对象是一样的:我认为它们的区别在于,在Java中所有对象的传递都是自动使用指针进行的,而在C语言中需要在某些地方添加星号和取地址符号。最近在谈话中,有人向我保证确实有区别!

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

10个回答

39

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的值只是一个引用,并且该引用以按值传递。这很令人困惑,因为“引用”一词在两个术语中都出现了,但它们是不同的东西。
您可能需要查看我关于C#参数传递的文章,以了解如果Java像C#一样具有传递引用的语义,将会发生什么情况,而且只有在使用ref关键字时才会出现。
您可能还想查看与该主题相关的我的Stack Overflow回答的评论。

2
非常真实。这是我见过的编程中最被误解的方面之一。 - mmcdole
是的,在C#中传递引用时按值传递是让我困惑的第一件事情之一。 - Ed S.
这表明了Joel(The Joel)对人们的看法 - 要么他们理解指针,要么他们不理解。要理解指针,你必须能够区分“引用”和其他引用=) - Chii
为什么你说 C 不能通过引用传递?C 和 C++ 都可以。对于 C:void foo(Type * val),调用者是 foo(&y)。这个调用将修改 y 的值。对于 C++:void foo(Type & val),调用者只需使用 foo(y)。 - Max
2
@Max:不,对于foo(&y)的情况,传递的是参数的值。那么参数是什么?是&y。该表达式获取了y的地址,然后将结果按值传递。C++确实有按引用传递的方式,但C语言并没有。 - Jon Skeet

7
我认为区别在于,在Java中所有对象的传递都自动使用指针完成,而在C中则需要在这里那里添加星号和取地址符。从概念上讲,这是非常正确的。如果我们严谨一点(这是一件好事),我们甚至可以说,在Java中根本没有传递对象。传递的只是“指针”,在Java中称为引用。所有的间接寻址都是自动完成的。因此,当你在C中使用"objref->foo"而不能使用点运算符"."时,因为你使用了指针,在Java中仍然可以使用点运算符,因为它不知道任何其他方式来访问成员。在C中,你可以传递指向对象的指针(在这里,它被实际称为指针),也可以传递对象本身,此时将传递一个副本。在C中,通过使用星号或"->"间接引用指针所指向的对象。在Java中,访问对象的唯一方式是使用点运算符(objref.member)。
如果我们再次严谨一些(现在又变得更加重要了),在Java和C中都不存在"按引用传递"。在Java中,我们传递的是对象的引用/指针,在C中,我们传递对象的副本(显然的情况),或者我们再次传递指向对象的指针的副本。因此,在这两种情况下——Java和C中——我们传递的都是对象的地址。不谈论原始的Java类型,在Java和C中都会复制它们——尽管在C中你也可以传递它们的地址,在那方面,聚合类型(即结构体)和"原始类型"之间没有任何区别。

3

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


2

一些事情

  • C没有引用(尽管C++有)
  • Java没有指针
  • 指针和引用的区别在于,指针实际上是可以计算的内存地址。而引用不能进行计算。
  • Java和C按值传递(在Java中传递对象时,引用会被复制到函数中)

我建议您从最后一条中删除“默认情况下”这个词 - C和Java按值传递,就这样。 - Jon Skeet

2
我认为指针是一个包含引用的变量。使用 & 可以获取内存地址(引用),使用 * 可以访问内存地址中的内容。我建议阅读书籍《C程序设计语言》(The C programming Language)
祝好!

2

真正的按引用传递意味着你可以给引用赋一个新值,这个新值将会在调用上下文中可见,就像在被调用的方法内部一样。

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


1

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

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

MyClass object1 = new MyClass();

MyClass object2 = object1;

即你可能认为object1和object2引用了不同的对象。然而,这是错误的。在此片段执行后,object1和object2将都引用同一个对象。如果您更改其中任何一个,则会影响另一个。

*您无法在初始化后更改对象的地址 即 MyClass obj =new MyClass(); 作为C++中的引用,您只能更改对象实例值

注意:Java提供了一个特殊功能 object1已设置为null,但object2仍指向原始对象。


1

如果你正在学习C语言(而不是C++),那么就没有引用。

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

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

如果您使用的是类似于C的C ++,则可以按引用传递,在这种情况下,您无需处理指针,并且函数中对变量的更改实际上直接更改外部值(除了您不能使其指向其他东西)。


嘿,可以这样说吗:“指针”是一种数据类型,“引用”只是处理数据的一种方式?一个是事物,另一个是技术?(我试图避免在这里使用“方法”的歧义) - Ziggy
指针肯定是一种数据类型。但是,在 C++ 中(大多数 C 人最终会使用),有一种称为引用的数据类型。这是一个问题,因为在 C++ 中实际上有两种“通过引用传递”的方式,即通过实际引用和通过指针。所以“按引用传递”是一种方式。 - Uri

1

最近Eric Lippert发表了一篇关于这个问题的文章。他是C#编译器的程序员,但他在那里说的话具有足够的普遍性,可以扩展到其他GC语言,如Java:

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

文章引用:

指针比引用严格来说“更强大”;你可以用指针做任何引用能做的事情。指针通常被实现为地址。地址是一个数字,它是进程整个虚拟地址空间中“字节数组”的偏移量。出于所有这些原因,我们在规范中不将引用描述为地址。规范只是说引用类型的变量“存储对对象的引用”,并完全模糊地说明了如何实现它。同样,指针变量存储“地址”,这也是相当模糊的。我们从未说过引用和地址是相同的。因此,在C#中,引用是一种让你引用对象的模糊东西。你不能对引用做任何事情,除了解引用它,并将其与另一个引用进行比较以检查是否相等。而在C#中,指针被识别为地址。与引用相比,指针包含地址,你可以做更多的事情。地址可以进行数学操作;你可以从一个地址中减去另一个地址,你可以将整数加到地址上等等。它们的合法操作表明它们是索引进程虚拟地址空间的“数组”的“高级数字”。

这是在讨论引用而不是“按引用传递” - 它们是相关但不同的概念。 - Jon Skeet

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,当您通过引用传递x调用Foo方法时,如果在Foo方法中将x增加1,则原始值也会增加1。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接