"通过引用传递"究竟是什么意思?

Dan*_*her 15 java parameters terminology pass-by-reference

谁有权决定?

编辑:显然我没有成功地完善我的问题.
不是在问Java的论证是如何传递的.我知道看起来像持有对象的变量实际上是一个包含对象引用的变量,并且该引用是按值传递的.这里有很多很好的解释(在链接的线程和其他线程中)和其他地方.

问题是关于术语传递的技术含义.(结束编辑)

我不确定这是否是SO的正确问题,如果没有道歉,但我不知道更好的地方.在这里的其他问题中已经说了很多,例如Java是"传递参考"还是"传递价值"?通过引用或通过值传递?,但我没有找到该术语含义的权威答案.

我认为"通过引用传递"意味着"将引用(通常是指针)传递给对象",因此被调用者可以修改调用者看到的对象,而"按值传递"意味着复制对象,并让被调用者玩得开心(显而易见的问题:如果对象包含引用,深层复制或浅层).
唱FW变成了很多 地方 说: "按引用传递"的意思只是说,这里有一些说法,这意味着更多的,但仍然定义读

一种ParameterPassing模式,其中将实际参数的引用(或者如果你想在政治上不正确,指针)传递给形式参数; 当被调用者需要形式参数时,它取消引用指针以获取它.

我没有发现许多地方给出了更强的术语定义,在这个页面上,我发现"形式参数的左值设置为实际参数的左值".并且,如果我理解正确,这里使用相同的定义("形式参数仅作为实际参数的别名.")

事实上,我发现使用更强定义的唯一地方是反对这样一种观点,即在Java中,对象是通过引用传递的(这可能是由于我缺乏google-fu).

所以,如果我把事情弄清楚,那就是通过参考传递

class Thing { ... }
void byReference(Thing object){ ... }
Thing something;
byReference(something);
Run Code Online (Sandbox Code Playgroud)

根据第一个定义大致对应(在C中)

struct RawThing { ... };
typedef RawThing *Thing;
void byReference(Thing object){
    // do something
}
// ...
struct RawThing whatever = blah();
Thing something = &whatever;
byReference(something); // pass whatever by reference
// we can change the value of what something (the reference to whatever) points to, but not
// where something points to
Run Code Online (Sandbox Code Playgroud)

从这个意义上讲,说Java通过引用传递对象就足够了.但根据第二个定义,传递参考意味着或多或少

struct RawThing { ... };
typedef RawThing *RawThingPtr;
typedef RawThingPtr *Thing;
void byReference(Thing object){
    // do something
}
// ...
RawThing whatever = blah();
RawThingPtr thing_pointer = &whatever;
byReference(&thing_pointer); // pass whatever by reference
// now we can not only change the pointed-to (referred) value,
// but also where thing_pointer points to
Run Code Online (Sandbox Code Playgroud)

而且由于Java只允许你有对象的指针(限制你可以用它们做什么)但是没有指针指针,从这个意义上说,Java通过引用传递对象是完全错误的.

所以,

  1. 我是否已充分理解上述传递参考的定义?
  2. 还有其他定义吗?
  3. 是否有共识,哪个定义是"正确的",如果是,哪个?

new*_*cct 8

当然,不同的人目前对"传递参考"的含义有不同的定义.这就是为什么他们不同意某些东西是否通过引用传递.

但是,无论您使用的定义,你必须使用它始终跨语言.你不能说一种语言具有传值,并且在另一种语言中具有完全相同的语义,并且说它是传递参考.指出语言之间的类比是解决这一争议的最佳方式,因为虽然人们可能对特定语言中的传递模式有强烈的意见,但当你将相同的语义与其他语言进行对比时,它有时会带来反直觉的结果,迫使他们重新思考他们的定义.

  • 一个主要观点是Java只是按值传递.(在Internet上随处搜索,您将找到这种观点.)此视图是对象不是值,但始终通过引用进行操作,因此它是按值分配或传递的引用.该视图认为,pass-by-reference的测试是否可以分配给调用范围中的变量.

如果一个人同意这个观点,那么人们也必须考虑大多数语言,包括Python,Ruby,OCaml,Scheme,Smalltalk,SML,Go,JavaScript,Objective-C等多种语言,作为传值.如果其中任何一个让你感到奇怪或违反直觉,我挑战你指出为什么你认为在Java中的任何语言中任何语言中的对象的语义不同.(我知道其中一些语言可能明确声称它们是传递引用;但它与它们所说的无关;必须根据实际行为对所有语言应用一致的定义.)

  • 如果您采用相反的观点,即Java中的对象是传递引用,那么您还必须将C视为传递引用.

以Java为例:

class Thing { int x; }
void func(Thing object){ object.x = 42; object = null; }
Thing something = null;
something = new Thing();
func(something);
Run Code Online (Sandbox Code Playgroud)

在C中,它将等同于:

typedef struct { int x; } Thing;
void func(Thing *object){ object->x = 42; object = NULL; }
Thing *something = NULL;
something = malloc(sizeof Thing);
memset(something, 0, sizeof(something));
func(something);
// later:
free(something);
Run Code Online (Sandbox Code Playgroud)

我声称上述内容在语义上是等价的; 只有语法不同.唯一的语法差异是:

  1. C要求显式*表示指针类型; Java的引用(指向对象的指针)类型不需要显式*.
  2. C用于->通过指针访问字段; Java只是使用.
  3. Java用于new为堆上的新对象动态分配内存; C用来malloc分配它,然后我们需要初始化内存.
  4. Java有垃圾收集

请注意,重要的是,

  1. 在这两种情况下func(something),使用对象调用函数的语法都是相同的:不需要执行任何操作,例如获取地址或任何内容.
  2. 在这两种情况下,对象都是动态分配的(它可能超出了函数的范围).和
  3. 在这两种情况下,object = null;函数内部都不会影响调用范围.

因此在两种情况下语义都是相同的,所以如果你调用Java pass-by-reference,你也必须调用C pass-by-reference.


rua*_*akh 5

这两个C示例实际上都演示了pass-by-value,因为C没有pass-by-reference.只是你传递的值是一个指针.通过引用传递的语言如Perl:

sub set_to_one($)
{
    $_[0] = 1; # set argument = 1
}
my $a = 0;
set_to_one($a); # equivalent to: $a = 1
Run Code Online (Sandbox Code Playgroud)

这里,变量$a实际上是通过引用传递的,因此子程序可以修改它.它没有修改一些$a通过间接指向的对象; 相反,它会改变$a自己.

在这方面,Java就像C一样,只是在Java对象中是"引用类型",所以你所拥有的(以及你所能传递的所有东西)都是指向它们的指针.像这样的东西:

void setToOne(Integer i)
{
    i = 1; // set argument = 1
}

void foo()
{
    Integer a = 0;
    setToOne(a); // has no effect
}
Run Code Online (Sandbox Code Playgroud)

实际上不会改变a; 它只会重新分配i.

  • @Voo:既然你说"请",我会指出`Foo*ptr2Foo = NULL;`是合法的,而`Foo&ref2Foo =*NULL;`触发未定义的行为.但实际上,你的评论毫无意义.是的,您可以重写使用引用的代码,并将其更改为使用指针; 是的,这基本上是C++编译器在内部所做的事情; 但不,这并不意味着"传递引用"和"传递值,值为指针/引用"是同义词.对于函数和调用者来说,语法和语义都是不同的. (2认同)

ibi*_*bid 5

谁有权决定?没有人,也没有人.你自己决定; 作家决定他或她的书; 读者决定是否同意作者.

要理解这个术语,需要深入了解语言(并根据C代码解释它们而不是忽视这一点).参数传递样式是指编译器通常用于创建特定行为的机制.通常定义以下内容:

  • 按值传递:在输入子例程时,参数将复制到参数中
  • 传递结果:输入子程序时参数未定义,子程序返回时参数复制到参数
  • pass by value-result:将参数复制到entry处的参数中,并将参数复制到返回的参数中
  • 通过引用传递:对参数变量的引用被复制到参数中; 对参数变量的任何访问都被透明地转换为对参数变量的访问

(术语注释:参数是子例程中定义的变量,参数是调用中使用的表达式.)

教科书通常也会按名称定义传递,但这种情况很少见,也不容易解释.通过需要也存在.

参数传递样式的重要性在于它的作用:在传递值时,对参数所做的任何更改都不会传递给参数; 在通过结果传递时,对参数所做的任何更改都会传递给最后的参数; 在通过引用传递时,对参数所做的任何更改都会在创建时传递给参数.

某些语言定义了多种传递样式,允许程序员分别为每个参数选择其首选样式.例如,在Pascal中,默认样式是按值传递,但程序员可以使用该var关键字指定按引用传递.其他一些语言指定一种传递方式.还有一些语言为不同类型指定不同的样式(例如,在C中,传递值是默认值,但数组是通过引用传递的).

现在,在Java中,从技术上讲,我们有一种带有pass-by-value的语言,对象变量的值是对象的引用.是否使Java传递引用的对象变量是一个品味问题.