按引用传递与按值传递有什么区别?

发布于 2024-07-10 15:21:35 字数 38 浏览 13 评论 0原文

参数“按引用”或“按值”传递是什么意思? 这些参数有何不同?

What does it mean to say that a parameter is passed "by reference" or "by value"? How do such parameters differ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

感性不性感 2024-07-17 15:21:35

首先也是最重要的,CS 理论中定义的“按值传递与按引用传递”区别现已过时,因为最初定义为“按引用传递”的技术已经过时了很受青睐,现在很少使用。1

较新的语言2倾向于使用不同(但相似)的技术来实现相同的效果(见下文)这是造成混乱的主要根源。

造成混淆的第二个原因是在“通过引用传递”中,“引用”的含义比一般术语“引用”更窄(因为该短语早于它)。


现在,真实的定义是:

  • 当参数通过引用传递时,调用者和被调用者对参数使用相同的变量。 如果被调用者修改参数变量,则效果对调用者的变量可见。

  • 当参数按值传递时,调用者和被调用者有两个自变量具有相同的值。 如果被调用者修改了参数变量,则调用者看不到效果。

在这个定义中需要注意的事情是:

  • 这里的“变量”是指调用者的(本地或全局)变量本身 - 即,如果我通过引用传递本地变量并分配给它,我'将更改调用者的变量本身,而不是例如它所指向的任何内容(如果它是指针)。

    • 现在这被认为是不好的做法(作为隐式依赖)。 因此,几乎所有较新的语言都是排他性或几乎排他性地按值传递。在函数所在的语言中,按引用传递现在主要以“输出/输入参数”的形式使用不能返回多个值。
  • “按引用传递”中“引用”的含义。 与一般“引用”术语的区别在于,此“引用”是临时且隐式的。被调用者得到的是一个与原始变量“相同”的“变量” .具体如何实现这种效果是无关紧要的(例如,语言还可能公开一些实现细节——地址、指针、解引用——这都是无关紧要的;如果最终效果是这样,那么它是按引用传递的)。


现在,在现代语言中,变量往往是“引用类型”(另一个比“引用传递”晚发明的概念并受其启发),即实际的对象数据单独存储在某处(通常,在堆上),并且只有对它的“引用”才会保存在变量中并作为参数传递。3

传递此类引用属于按值传递,因为从技术上讲,变量的值是引用本身,而不是引用的对象。 但是,对程序的最终效果可以与按值传递或按引用传递相同:

  • 如果仅从调用者的变量中获取引用并作为参数传递,则此与传递引用具有相同的效果:如果引用的对象在被调用者中发生变化,则调用者将看到更改。
    • 但是,如果保存此引用的变量被重新分配,它将停止指向该对象,因此对此变量的任何进一步操作都将影响它现在指向的任何内容。
  • 为了具有与按值传递相同的效果,会在某个时刻创建对象的副本。 选项包括:
    • 调用者可以在调用之前制作一个私有副本,然后为被调用者提供对该副本的引用。
    • 在某些语言中,某些对象类型是“不可变的”:对它们进行的任何看似改变值的操作都会创建一个全新的对象,而不会影响原始对象。 因此,传递这种类型的对象作为参数总是具有按值传递的效果:如果需要更改,则被调用者将自动创建一个副本,并且调用者的对象永远不会受到影响。
      • 在函数式语言中,所有对象都是不可变的。

正如您所看到的,这对技术与定义中的技术几乎相同,只是有一定程度的间接性:只需将“变量”替换为“引用对象”即可。

没有达成一致的名称对于他们来说,这会导致扭曲的解释,例如“按值调用,其中值是引用”。 1975 年,Barbara Liskov 提出了术语“call-by-object-sharing”(或者有时只是“通过共享来电”)尽管它从未完全流行起来。 此外,这两个短语都与原来的短语没有相似之处。 难怪旧术语最终在没有更好的情况下被重复使用,从而导致混乱。4

(我会使用术语“新”“间接” “新技术的传递值/传递引用。)


注意:很长一段时间以来,这个答案常常说:

假设我想与您共享一个网页。 如果我告诉你网址,我就
通过引用传递。 您可以使用该 URL 查看与我相同的网页
可以看到。 如果该页面发生更改,我们都会看到更改。 如果你
删除 URL,您所做的就是销毁对该 URL 的引用
页面 - 您不会删除实际页面本身。

如果我打印该页并给你打印输出,我就路过
价值。 您的页面是原始页面的断开副本。 你不会看到
任何后续更改以及您所做的任何更改(例如乱涂乱画)
在您的打印输出上)将不会显示在原始页面上。 如果你
销毁打印输出,您就销毁了您的副本
对象 - 但原始网页保持不变。

大部分是正确的除了“引用”的狭义含义——它既是临时的又是隐式的(不是必须的,但是显式的和/或持久的是附加功能,不是按引用传递语义的一部分,如上所述)。 更接近的类比是给您一份文档的副本与邀请您处理原件。


1除非您使用 Fortran 或 Visual Basic 进行编程,否则这不是默认行为,并且在现代使用的大多数语言中,真正的按引用调用甚至是不可能的。

2相当多的老版本也支持它

3在几种现代语言中,所有类型都是引用类型。 这种方法由 CLU 语言于 1975 年首创,此后被许多其他语言采用,包括 Python 和 Ruby。 还有更多语言使用混合方法,其中一些类型是“值类型”,另一些类型是“引用类型”——其中包括 C#、Java 和 JavaScript。

4回收一个合适的旧术语本身并没有什么坏处,但是人们必须以某种方式弄清楚每次使用的含义。 不这样做正是令人困惑的地方。

First and foremost, the "pass by value vs. pass by reference" distinction as defined in the CS theory is now obsolete because the technique originally defined as "pass by reference" has since fallen out of favor and is seldom used now.1

Newer languages2 tend to use a different (but similar) pair of techniques to achieve the same effects (see below) which is the primary source of confusion.

A secondary source of confusion is the fact that in "pass by reference", "reference" has a narrower meaning than the general term "reference" (because the phrase predates it).


Now, the authentic definition is:

  • When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller's variable.

  • When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.

Things to note in this definition are:

  • "Variable" here means the caller's (local or global) variable itself -- i.e. if I pass a local variable by reference and assign to it, I'll change the caller's variable itself, not e.g. whatever it is pointing to if it's a pointer.

    • This is now considered bad practice (as an implicit dependency). As such, virtually all newer languages are exclusively, or almost exclusively pass-by-value. Pass-by-reference is now chiefly used in the form of "output/input arguments" in languages where a function cannot return more than one value.
  • The meaning of "reference" in "pass by reference". The difference with the general "reference" term is that this "reference" is temporary and implicit. What the callee gets is a "variable" that is somehow "the same" as the original one. How specifically this effect is achieved is irrelevant (e.g. the language may also expose some implementation details -- addresses, pointers, dereferencing -- this is all irrelevant; if the net effect is this, it's pass-by-reference).


Now, in modern languages, variables tend to be of "reference types" (another concept invented later than "pass by reference" and inspired by it), i.e. the actual object data is stored separately somewhere (usually, on the heap), and only "references" to it are ever held in variables and passed as parameters.3

Passing such a reference falls under pass-by-value because a variable's value is technically the reference itself, not the referred object. However, the net effect on the program can be the same as either pass-by-value or pass-by-reference:

  • If a reference is just taken from a caller's variable and passed as an argument, this has the same effect as pass-by-reference: if the referred object is mutated in the callee, the caller will see the change.
    • However, if a variable holding this reference is reassigned, it will stop pointing to that object, so any further operations on this variable will instead affect whatever it is pointing to now.
  • To have the same effect as pass-by-value, a copy of the object is made at some point. Options include:
    • The caller can just make a private copy before the call and give the callee a reference to that instead.
    • In some languages, some object types are "immutable": any operation on them that seems to alter the value creates a completely new object without affecting the original one. So, passing an object of such a type as an argument always has the effect of pass-by-value: a copy for the callee will be made automatically if and when it needs a change, and the caller's object will never be affected.
      • In functional languages, all objects are immutable.

As you may see, this pair of techniques is almost the same as those in the definition, only with a level of indirection: just replace "variable" with "referenced object".

There's no agreed-upon name for them, which leads to contorted explanations like "call by value where the value is a reference". In 1975, Barbara Liskov suggested the term "call-by-object-sharing" (or sometimes just "call-by-sharing") though it never quite caught on. Moreover, neither of these phrases draws a parallel with the original pair. No wonder the old terms ended up being reused in the absence of anything better, leading to confusion.4

(I would use the terms "new" or "indirect" pass-by-value/pass-by-reference for the new techniques.)


NOTE: For a long time, this answer used to say:

Say I want to share a web page with you. If I tell you the URL, I'm
passing by reference. You can use that URL to see the same web page I
can see. If that page is changed, we both see the changes. If you
delete the URL, all you're doing is destroying your reference to that
page - you're not deleting the actual page itself.

If I print out the page and give you the printout, I'm passing by
value. Your page is a disconnected copy of the original. You won't see
any subsequent changes, and any changes that you make (e.g. scribbling
on your printout) will not show up on the original page. If you
destroy the printout, you have destroyed your copy of the
object - but the original web page remains intact.

This is mostly correct except the narrower meaning of "reference" -- it being both temporary and implicit (it doesn't have to, but being explicit and/or persistent are additional features, not a part of the pass-by-reference semantic, as explained above). A closer analogy would be giving you a copy of a document vs inviting you to work on the original.


1Unless you are programming in Fortran or Visual Basic, it's not the default behavior, and in most languages in modern use, true call-by-reference is not even possible.

2A fair amount of older ones support it, too

3In several modern languages, all types are reference types. This approach was pioneered by the language CLU in 1975 and has since been adopted by many other languages, including Python and Ruby. And many more languages use a hybrid approach, where some types are "value types" and others are "reference types" -- among them are C#, Java, and JavaScript.

4There's nothing bad with recycling a fitting old term per se, but one has to somehow make it clear which meaning is used each time. Not doing that is exactly what keeps confusing.

如梦 2024-07-17 15:21:35

这是一种将参数传递给函数的方法。 通过引用传递意味着被调用函数的参数将与调用者传递的参数相同(不是值,而是标识 - 变量本身)。 按值传递意味着被调用函数的参数将是调用者传递的参数的副本。 值将相同,但身份(变量)不同。 因此,在一种情况下,对被调用函数完成的参数的更改会更改传递的参数,而在另一种情况下,只会更改被调用函数中参数的值(这只是一个副本)。 急急急:

  • Java只支持按值传递。 始终复制参数,即使在复制对对象的引用时,被调用函数中的参数将指向同一对象,并且调用者将看到对该对象的更改。 由于这可能会令人困惑,这里是 Jon Skeet 对此的看法。
  • C# 支持按值传递和按引用传递(在调用方和被调用函数处使用关键字ref)。 Jon Skeet 在此处对此也有很好的解释。
  • C++ 支持按值传递和按引用传递(在被调用函数中使用的引用参数类型)。 您将在下面找到对此的解释。

代码

因为我的语言是 C++,所以我将在这里使用它

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

,Java 中的示例也不会造成伤害:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

维基百科

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

这家伙几乎做到了:

http://javadude.com/articles/passbyvalue.htm

It's a way how to pass arguments to functions. Passing by reference means the called functions' parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself). Pass by value means the called functions' parameter will be a copy of the callers' passed argument. The value will be the same, but the identity - the variable - is different. Thus changes to a parameter done by the called function in one case changes the argument passed and in the other case just changes the value of the parameter in the called function (which is only a copy). In a quick hurry:

  • Java only supports pass by value. Always copies arguments, even though when copying a reference to an object, the parameter in the called function will point to the same object and changes to that object will be see in the caller. Since this can be confusing, here is what Jon Skeet has to say about this.
  • C# supports pass by value and pass by reference (keyword ref used at caller and called function). Jon Skeet also has a nice explanation of this here.
  • C++ supports pass by value and pass by reference (reference parameter type used at called function). You will find an explanation of this below.

Codes

Since my language is C++, i will use that here

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

And an example in Java won't hurt:

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

Wikipedia

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

This guy pretty much nails it:

http://javadude.com/articles/passbyvalue.htm

慢慢从新开始 2024-07-17 15:21:35

这里的许多答案(特别是得票最高的答案)实际上是不正确的,因为他们误解了“通过​​引用调用”的真正含义。 这是我试图澄清问题的尝试。

TL;DR

用最简单的话来说:

  • 按值调用意味着您将作为函数参数传递
  • 按引用调用意味着您传递变量 作为函数参数

用隐喻术语来说:

  • 按值调用我在一张纸上写下一些内容并将其交给您。 也许它是一个网址,也许它是《战争与和平》的完整副本。 不管它是什么,它都在我给你的一张纸上,所以现在它实际上是你的一张纸。 现在你可以自由地在那张纸上乱写乱画,或者用那张纸在其他地方找到一些东西并摆弄它,等等。
  • 参考调用是指我给你我的笔记本,里面写了一些东西。 你可以在我的笔记本上乱写乱画(也许我希望你这样做,也许我不想),然后我会保留我的笔记本,无论你在笔记本上乱写什么。 另外,如果你或我写的内容中有关于如何在其他地方找到某些东西的信息,你或我可以去那里摆弄这些信息。

“按值调用”和“按引用调用”的含义请

注意,这两个概念与引用类型的概念完全独立且正交(在 Java 中是 Object 的所有子类型,在 C# 中是所有 class 类型),或指针的概念像 C 中的类型(在语义上等同于 Java 的“引用类型”,只是语法不同)。

引用类型的概念对应于一个 URL:它本身既是一条信息,又是一个引用(一个指针,如果你会)其他信息。 您可以在不同的地方拥有同一个 URL 的多个副本,并且它们不会更改它们链接到的网站; 如果网站更新,则每个 URL 副本仍会指向更新的信息。 相反,在任何一处更改 URL 都不会影响该 URL 的任何其他书面副本。

请注意,C++ 有一个“引用”概念(例如 int&),它类似于 Java 和 C# 的“引用类型”,但就像“通过引用调用”一样。 Java 和 C# 的“引用类型”以及 Python 中的所有类型都类似于 C 和 C++ 所谓的“指针类型”(例如 int*)。


好的,这是更长、更正式的解释。

术语

首先,我想强调一些重要的术语,以帮助澄清我的答案,并确保我们在使用单词时所指的是相同的想法。 (实际上,我相信对此类主题的绝大多数混淆源于使用单词的方式未能完全传达预期的含义。)

首先,这里有一个函数声明的类似 C 语言的示例:

void foo(int param) {  // line 1
    param += 1;
}

这是一个调用这个函数的例子:

void bar() {
    int arg = 1;  // line 2
    foo(arg);     // line 3
}

使用这个例子,我想定义一些重要的术语:

  • foo是一个在第1行声明的函数(Java坚持使所有函数成为方法,但概念是相同的,但不失一般性;C 和 C++ 对声明和定义进行了区分,我不会在这里讨论)
  • param 是一个形式参数foo,也在第 1 行声明
  • arg 是一个变量,具体来说是一个局部变量在第 2 行 arg 上声明和初始化的函数 bar
  • 也是 的特定调用参数 >foo on line 3

这里有两组非常重要的概念需要区分。 第一个是变量

  • 计算表达式的结果 在语言中。 例如,在上面的 bar 函数中,在 int arg = 1; 行之后,表达式 arg 具有1
  • 变量值的容器。 变量可以是可变的(这是大多数类 C 语言中的默认设置)、只读(例如使用 Java 的 final 或 C# 的 readonly 声明)或深度不可变(例如使用 C++ 的 const)。

需要区分的另一对重要概念是参数参数

  • 参数(也称为形式参数)是一个变量,在调用函数时必须由调用者提供。
  • 参数是由函数调用者提供的,以满足该函数的特定形式参数

调用按值调用

>按值调用,函数的形式参数是为函数调用新创建的变量,并使用其参数的进行初始化。

这与用值初始化任何其他类型的变量的工作方式完全相同。 例如:

int arg = 1;
int another_variable = arg;

这里 arganother_variable 是完全独立的变量——它们的值可以彼此独立地改变。 然而,在声明 another_variable 时,它被初始化为与 arg 保持相同的值 - 即 1

由于它们是自变量,因此对 another_variable 的更改不会影响 arg

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

这与 argparam 之间的关系完全相同 在我们上面的例子中,为了对称,我将在这里重复一下:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

这就像我们这样编写代码一样:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

也就是说,按值调用的定义特征是被调用者(在本例中是 foo)接收作为参数,但对于来自 的变量的这些值有自己单独的变量调用者(在本例中为bar)。

回到上面的比喻,如果我是 bar 而你是 foo,当我打电话给你时,我会递给你一张带有值的纸上面写着。 您将这张纸称为param。 该值是我在笔记本中写入的值(我的局部变量)的副本,位于我称为 arg 的变量中。

(顺便说一句:根据硬件和操作系统的不同,关于如何从一个函数调用另一个函数,存在各种调用约定。调用约定就像我们决定是否将值写入我的一块纸然后递给你,或者如果你有一张我写在上面的纸,或者我把它写在我们面前的墙上,这也是一个有趣的话题,但远远超出了范围。 。

通过引用调用

通过引用调用中,函数的形式参数只是调用者作为参数提供的相同变量的新名称

回到上面的示例,它相当于:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

由于 param 只是 arg 的另一个名称 - 也就是说,它们是相同的变量 ,对 param 的更改会反映在 arg 中。 这是按引用调用与按值调用不同的基本方式。

很少有语言支持按引用调用,但 C++ 可以这样做:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

在这种情况下,param 不仅仅具有与 arg 相同的 code>,它实际上 arg(只是名称不同),因此 bar 可以观察到 arg 有已增加。

请注意,这不是 Java、JavaScript、C、Objective-C、Python 或当今几乎任何其他流行语言的工作方式。 这意味着这些语言不是通过引用调用,而是通过值调用。

附录:通过对象共享调用

如果您所拥有的是按值调用,但实际值是引用类型指针类型,那么“值”本身并不是很有趣(例如,在 C 中它只是特定于平台的大小的整数)——有趣的是该值指向

如果引用类型(即指针)指向的是可变的,那么可能会产生有趣的效果:您可以修改指向的值,并且调用者可以观察到指向的值的变化,即使调用者无法观察到指针本身的变化。

再次借用 URL 的类比,如果我们都关心的是网站而不是 URL,那么我向您提供网站 URL 的副本这一事实并不是特别有趣。 事实上,您在 URL 副本上乱涂乱画不会影响我的 URL 副本,这并不是我们关心的事情(事实上,在 Java 和 Python 等语言中,“URL”或引用类型值可以根本不能修改,只有它指向的东西可以)。

Barbara Liskov 在发明 CLU 编程语言(具有这些语义)时意识到现有术语“按值调用”和“按引用调用”对于描述这种新语言的语义并不是特别有用。 因此她发明了一个新术语:通过对象共享调用

当讨论技术上按值调用的语言时,但使用的常见类型是引用或指针类型(即:几乎所有现代命令式、面向对象或多范式编程语言),我发现它不会那么令人困惑只需避免谈论按值调用按引用调用即可。 坚持通过对象共享调用(或简单地通过对象调用),没有人会感到困惑。 :-)

Many answers here (and in particular the most highly upvoted answer) are factually incorrect, since they misunderstand what "call by reference" really means. Here's my attempt to set matters straight.

TL;DR

In simplest terms:

  • call by value means that you pass values as function arguments
  • call by reference means that you pass variables as function arguments

In metaphoric terms:

  • Call by value is where I write down something on a piece of paper and hand it to you. Maybe it's a URL, maybe it's a complete copy of War and Peace. No matter what it is, it's on a piece of paper which I've given to you, and so now it is effectively your piece of paper. You are now free to scribble on that piece of paper, or use that piece of paper to find something somewhere else and fiddle with it, whatever.
  • Call by reference is when I give you my notebook which has something written down in it. You may scribble in my notebook (maybe I want you to, maybe I don't), and afterwards I keep my notebook, with whatever scribbles you've put there. Also, if what either you or I wrote there is information about how to find something somewhere else, either you or I can go there and fiddle with that information.

What "call by value" and "call by reference" don't mean

Note that both of these concepts are completely independent and orthogonal from the concept of reference types (which in Java is all types that are subtypes of Object, and in C# all class types), or the concept of pointer types like in C (which are semantically equivalent to Java's "reference types", simply with different syntax).

The notion of reference type corresponds to a URL: it is both itself a piece of information, and it is a reference (a pointer, if you will) to other information. You can have many copies of a URL in different places, and they don't change what website they all link to; if the website is updated then every URL copy will still lead to the updated information. Conversely, changing the URL in any one place won't affect any other written copy of the URL.

Note that C++ has a notion of "references" (e.g. int&) that is not like Java and C#'s "reference types", but is like "call by reference". Java and C#'s "reference types", and all types in Python, are like what C and C++ call "pointer types" (e.g. int*).


OK, here's the longer and more formal explanation.

Terminology

To start with, I want to highlight some important bits of terminology, to help clarify my answer and to ensure we're all referring to the same ideas when we are using words. (In practice, I believe the vast majority of confusion about topics such as these stems from using words in ways that to not fully communicate the meaning that was intended.)

To start, here's an example in some C-like language of a function declaration:

void foo(int param) {  // line 1
    param += 1;
}

And here's an example of calling this function:

void bar() {
    int arg = 1;  // line 2
    foo(arg);     // line 3
}

Using this example, I want to define some important bits of terminology:

  • foo is a function declared on line 1 (Java insists on making all functions methods, but the concept is the same without loss of generality; C and C++ make a distinction between declaration and definition which I won't go into here)
  • param is a formal parameter to foo, also declared on line 1
  • arg is a variable, specifically a local variable of the function bar, declared and initialized on line 2
  • arg is also an argument to a specific invocation of foo on line 3

There are two very important sets of concepts to distinguish here. The first is value versus variable:

  • A value is the result of evaluating an expression in the language. For example, in the bar function above, after the line int arg = 1;, the expression arg has the value 1.
  • A variable is a container for values. A variable can be mutable (this is the default in most C-like languages), read-only (e.g. declared using Java's final or C#'s readonly) or deeply immutable (e.g. using C++'s const).

The other important pair of concepts to distinguish is parameter versus argument:

  • A parameter (also called a formal parameter) is a variable which must be supplied by the caller when calling a function.
  • An argument is a value that is supplied by the caller of a function to satisfy a specific formal parameter of that function

Call by value

In call by value, the function's formal parameters are variables that are newly created for the function invocation, and which are initialized with the values of their arguments.

This works exactly the same way that any other kinds of variables are initialized with values. For example:

int arg = 1;
int another_variable = arg;

Here arg and another_variable are completely independent variables -- their values can change independently of each other. However, at the point where another_variable is declared, it is initialized to hold the same value that arg holds -- which is 1.

Since they are independent variables, changes to another_variable do not affect arg:

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

This is exactly the same as the relationship between arg and param in our example above, which I'll repeat here for symmetry:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

It is exactly as if we had written the code this way:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

That is, the defining characteristic of what call by value means is that the callee (foo in this case) receives values as arguments, but has its own separate variables for those values from the variables of the caller (bar in this case).

Going back to my metaphor above, if I'm bar and you're foo, when I call you, I hand you a piece of paper with a value written on it. You call that piece of paper param. That value is a copy of the value I have written in my notebook (my local variables), in a variable I call arg.

(As an aside: depending on hardware and operating system, there are various calling conventions about how you call one function from another. The calling convention is like us deciding whether I write the value on a piece of my paper and then hand it to you, or if you have a piece of paper that I write it on, or if I write it on the wall in front of both of us. This is an interesting subject as well, but far beyond the scope of this already long answer.)

Call by reference

In call by reference, the function's formal parameters are simply new names for the same variables that the caller supplies as arguments.

Going back to our example above, it's equivalent to:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

Since param is just another name for arg -- that is, they are the same variable, changes to param are reflected in arg. This is the fundamental way in which call by reference differs from call by value.

Very few languages support call by reference, but C++ can do it like this:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

In this case, param doesn't just have the same value as arg, it actually is arg (just by a different name) and so bar can observe that arg has been incremented.

Note that this is not how any of Java, JavaScript, C, Objective-C, Python, or nearly any other popular language today works. This means that those languages are not call by reference, they are call by value.

Addendum: call by object sharing

If what you have is call by value, but the actual value is a reference type or pointer type, then the "value" itself isn't very interesting (e.g. in C it's just an integer of a platform-specific size) -- what's interesting is what that value points to.

If what that reference type (that is, pointer) points to is mutable then an interesting effect is possible: you can modify the pointed-to value, and the caller can observe changes to the pointed-to value, even though the caller cannot observe changes to the pointer itself.

To borrow the analogy of the URL again, the fact that I gave you a copy of the URL to a website is not particularly interesting if the thing we both care about is the website, not the URL. The fact that you scribbling over your copy of the URL doesn't affect my copy of the URL isn't a thing we care about (and in fact, in languages like Java and Python the "URL", or reference type value, can't be modified at all, only the thing pointed to by it can).

Barbara Liskov, when she invented the CLU programming language (which had these semantics), realized that the existing terms "call by value" and "call by reference" weren't particularly useful for describing the semantics of this new language. So she invented a new term: call by object sharing.

When discussing languages that are technically call by value, but where common types in use are reference or pointer types (that is: nearly every modern imperative, object-oriented, or multi-paradigm programming language), I find it's a lot less confusing to simply avoid talking about call by value or call by reference. Stick to call by object sharing (or simply call by object) and nobody will be confused. :-)

诗笺 2024-07-17 15:21:35

在理解这两个术语之前,您必须了解以下内容。 每个物体都有两个东西可以使它被区分。

  • 它的价值。
  • 它的地址。

因此,如果您说 employee.name = "John",请知道关于 name 有两件事。 它的值是“John”,它在内存中的位置是一些十六进制数字,可能如下所示:0x7fd5d258dd00

根据语言的体系结构或对象的类型(类、结构等),您可以传输 "John"0x7fd5d258dd00

传递"John" 称为按值传递。

传递 0x7fd5d258dd00 称为按引用传递。 任何指向此内存位置的人都可以访问“John”的值。

有关这方面的更多信息,我建议您阅读取消引用指针以及为什么选择结构(值类型)而不是类(引用类型)

Before understanding the two terms, you must understand the following. Every object has two things that can make it be distinguished.

  • Its value.
  • Its address.

So if you say employee.name = "John", know that there are two things about name. Its value which is "John" and also its location in the memory which is some hexadecimal number maybe like this: 0x7fd5d258dd00.

Depending on the language's architecture or the type (class, struct, etc.) of your object, you would be either transferring "John" or 0x7fd5d258dd00

Passing "John" is known as passing by value.

Passing 0x7fd5d258dd00 is known as passing by reference. Anyone who is pointing to this memory location will have access to the value of "John".

For more on this, I recommend you to read about dereferencing a pointer and also why choose struct (value type) over class (reference type).

萌逼全场 2024-07-17 15:21:35

这是一个例子:

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

Here is an example:

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}
雪化雨蝶 2024-07-17 15:21:35

获取此信息的最简单方法是使用 Excel 文件。 举例来说,您在单元格 A1 和 B1 中有两个数字 5 和 2,并且您想在第三个单元格(假设为 A2)中找到它们的总和。

您可以通过两种方式执行此操作。

  • 通过在单元格中输入 = 5 + 2将它们的值传递到单元格 A2。 在这种情况下,如果单元格 A1 或 B1 的值发生变化,A2 中的总和保持不变。

  • 或者通过输入= A1 + B1将单元格 A1 和 B1 的“引用”传递给单元格 A2。 在这种情况下,如果单元格 A1 或 B1 的值发生变化,A2 中的总和也会发生变化。

The simplest way to get this is on an Excel file. Let’s say for example that you have two numbers, 5 and 2 in cells A1 and B1 accordingly, and you want to find their sum in a third cell, let's say A2.

You can do this in two ways.

  • Either by passing their values to cell A2 by typing = 5 + 2 into this cell. In this case, if the values of the cells A1 or B1 change, the sum in A2 remains the same.

  • Or by passing the “references” of the cells A1 and B1 to cell A2 by typing = A1 + B1. In this case, if the values of the cells A1 or B1 change, the sum in A2 changes too.

农村范ル 2024-07-17 15:21:35

当通过引用传递时,您基本上是在传递一个指向变量的指针。 按值传递您正在传递变量的副本。

在基本用法中,这通常意味着按引用传递,对变量的更改将在调用方法中看到,而按值传递则不会。

When passing by reference you are basically passing a pointer to the variable. Pass by value you are passing a copy of the variable.

In basic usage this normally means pass by reference, changes to the variable will seen be in the calling method and in pass by value they won’t.

橙味迷妹 2024-07-17 15:21:35

按值传递发送存储在您指定的变量中的数据的副本,而按引用传递则发送到变量本身的直接链接。

因此,如果您通过引用传递变量,然后更改传递到的块内的变量,则原始变量将被更改。 如果您只是按值传递,则原始变量将无法被您传递到的块更改,但您将获得调用时它包含的任何内容的副本。

Pass by value sends a copy of the data stored in the variable you specify, and pass by reference sends a direct link to the variable itself.

So if you pass a variable by reference and then change the variable inside the block you passed it into, the original variable will be changed. If you simply pass by value, the original variable will not be able to be changed by the block you passed it into, but you will get a copy of whatever it contained at the time of the call.

甜是你 2024-07-17 15:21:35

看看这张照片:

在第一种情况(通过引用传递)中,当函数内部设置或更改变量时,外部变量也会更改。

但在第二种情况下(按值传递),更改函数内部的变量不会对外部变量产生任何影响。

要阅读本文,请参阅此链接

通过引用调用 vs按值调用

Take a look at this photo:

In the first case (pass by reference), when the variable is set or changed inside the function, the external variable also changes.

But in the second case (pass by value), changing the variable inside the function doesn't have any effect on the external variable.

For reading the article, see this link.

Call by reference vs call by value

メ斷腸人バ 2024-07-17 15:21:35

按值传递 - 函数复制变量并使用副本(因此它不会更改原始变量中的任何内容)

按引用传递 - 函数使用原始变量。 如果更改另一个函数中的变量,原始变量也会更改。

示例(复制并使用/自己尝试一下看看):

#include <iostream>

using namespace std;

void funct1(int a) // Pass-by-value
{
    a = 6; // Now "a" is 6 only in funct1, but not in main or anywhere else
}

void funct2(int &a)  // Pass-by-reference
{
    a = 7; // Now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout << endl << "A is currently " << a << endl << endl; // Will output 5
    funct2(a);
    cout << endl << "A is currently " << a << endl << endl; // Will output 7

    return 0;
}

保持简单,偷看。 文字墙可能是一个坏习惯。

Pass by value - The function copies the variable and works with a copy (so it doesn't change anything in the original variable)

Pass by reference - The function uses the original variable. If you change the variable in the other function, it changes in the original variable too.

Example (copy and use/try this yourself and see):

#include <iostream>

using namespace std;

void funct1(int a) // Pass-by-value
{
    a = 6; // Now "a" is 6 only in funct1, but not in main or anywhere else
}

void funct2(int &a)  // Pass-by-reference
{
    a = 7; // Now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout << endl << "A is currently " << a << endl << endl; // Will output 5
    funct2(a);
    cout << endl << "A is currently " << a << endl << endl; // Will output 7

    return 0;
}

Keep it simple, peeps. Walls of text can be a bad habit.

我偏爱纯白色 2024-07-17 15:21:35

它们之间的主要区别在于值类型变量存储值,因此在方法调用中指定值类型变量会将该变量值的副本传递给该方法。 引用类型变量存储对对象的引用,因此将引用类型变量指定为参数会向方法传递引用该对象的实际引用的副本。 即使引用本身是按值传递的,该方法仍然可以使用它接收到的引用来与原始对象进行交互,并可能修改原始对象。 类似地,当通过 return 语句从方法返回信息时,该方法返回存储在值类型变量中的值的副本或存储在引用类型变量中的引用的副本。 返回引用时,调用方法可以使用该引用与引用的对象进行交互。 因此,实际上,对象总是通过引用传递。

在 C# 中,要通过引用传递变量,以便被调用的方法可以修改变量,C# 提供了关键字 ref 和 out。 将 ref 关键字应用于参数声明允许您通过引用将变量传递给方法 - 被调用的方法将能够修改调用者中的原始变量。 ref 关键字用于已在调用方法中初始化的变量。 通常,当方法调用包含未初始化的变量作为参数时,编译器会生成错误。 在参数前面加上关键字 out 将创建一个输出参数。 这向编译器表明参数将通过引用传递到被调用的方法中,并且被调用的方法将为调用者中的原始变量赋值。 如果该方法未在每个可能的执行路径中为输出参数赋值,则编译器会生成错误。 这还可以防止编译器为作为参数传递给方法的未初始化变量生成错误消息。 一种方法只能通过 return 语句向其调用者返回一个值,但可以通过指定多个输出(ref 和/或 out)参数来返回多个值。

请参阅此处的 C# 讨论和示例 链接文本

A major difference between them is that value-type variables store values, so specifying a value-type variable in a method call passes a copy of that variable's value to the method. Reference-type variables store references to objects, so specifying a reference-type variable as an argument passes the method a copy of the actual reference that refers to the object. Even though the reference itself is passed by value, the method can still use the reference it receives to interact with—and possibly modify—the original object. Similarly, when returning information from a method via a return statement, the method returns a copy of the value stored in a value-type variable or a copy of the reference stored in a reference-type variable. When a reference is returned, the calling method can use that reference to interact with the referenced object. So, in effect, objects are always passed by reference.

In c#, to pass a variable by reference so the called method can modify the variable's, C# provides keywords ref and out. Applying the ref keyword to a parameter declaration allows you to pass a variable to a method by reference—the called method will be able to modify the original variable in the caller. The ref keyword is used for variables that already have been initialized in the calling method. Normally, when a method call contains an uninitialized variable as an argument, the compiler generates an error. Preceding a parameter with keyword out creates an output parameter. This indicates to the compiler that the argument will be passed into the called method by reference and that the called method will assign a value to the original variable in the caller. If the method does not assign a value to the output parameter in every possible path of execution, the compiler generates an error. This also prevents the compiler from generating an error message for an uninitialized variable that is passed as an argument to a method. A method can return only one value to its caller via a return statement, but can return many values by specifying multiple output (ref and/or out) parameters.

see c# discussion and examples here link text

月朦胧 2024-07-17 15:21:35

示例:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const & 通常是最好的。 您不会遭受建设和破坏惩罚。 如果引用不是 const,则您的界面建议它将更改传入的数据。

Examples:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const & is generally best. You don't incur the construction and destruction penalty. If the reference isn't const your interface is suggesting that it will change the passed in data.

风吹雪碎 2024-07-17 15:21:35

如果您不想在将原始变量传递给函数后更改其值,则应使用“按值传递”参数构造该函数。

那么该函数将只有该值,但没有传入变量的地址。 如果没有变量的地址,函数内部的代码就无法更改从函数外部看到的变量值。

但是,如果您想让函数从外部看到能够更改变量值,则需要使用按引用传递。 由于值和地址(引用)都被传入并且在函数内部可用。

If you don't want to change the value of the original variable after passing it into a function, the function should be constructed with a "pass by value" parameter.

Then the function will have only the value, but not the address of the passed in variable. Without the variable's address, the code inside the function cannot change the variable value as seen from the outside of the function.

But if you want to give the function the ability to change the value of the variable as seen from the outside, you need to use pass by reference. As both the value and the address (reference) are passed in and are available inside the function.

感情洁癖 2024-07-17 15:21:35

简而言之,按值传递就是它是什么,按引用传递就是它在哪里。

如果您的值是 VAR1 =“Happy Guy!”,您只会看到“Happy Guy!”。 如果 VAR1 变成“Happy Gal!”,你就不会知道这一点。 如果它是通过引用传递的,并且 VAR1 发生变化,那么您就会发生变化。

In short, Passed by value is WHAT it is and passed by reference is WHERE it is.

If your value is VAR1 = "Happy Guy!", you will only see "Happy Guy!". If VAR1 changes to "Happy Gal!", you won't know that. If it's passed by reference, and VAR1 changes, you will.

屌丝范 2024-07-17 15:21:35

1. 按值传递/按值调用

   void printvalue(int x) 
   {
       x = x + 1 ;
       cout << x ;  // 6
   }

   int x = 5;
   printvalue(x);
   cout << x;    // 5

在按值调用中,当您将值传递给 printvalue(x)(即参数 5)时,它会被复制到void printvalue(int x)。 现在,我们有两个不同的值 5 和复制的值 5,这两个值存储在不同的内存位置。 因此,如果您在 void printvalue(int x) 中进行任何更改,它不会反映回参数。

2. 按引用传递/按引用调用

   void printvalue(int &x) 
   {
      x = x + 1 ;
      cout << x ; // 6
   }

   int x = 5;
   printvalue(x);
   cout << x;   // 6

在按引用调用中,只有一处区别。 我们使用 & 即地址运算符。 通过做
void printvalue(int &x) 我们引用的是 x 的地址,它告诉我们它们都引用相同的位置。 因此,函数内部所做的任何更改都会反映在外部。

既然您已经来到这里,您还应该了解...

3。 按指针传递/按地址调用

   void printvalue(int* x) 
   {
      *x = *x + 1 ;
      cout << *x ; // 6
   }

   int x = 5;
   printvalue(&x);
   cout << x;   // 6

在按地址传递中,指针int* x 保存传递给它的地址printvalue(&x)。 因此,函数内部所做的任何更改都会反映在外部。

1. Pass By Value / Call By Value

   void printvalue(int x) 
   {
       x = x + 1 ;
       cout << x ;  // 6
   }

   int x = 5;
   printvalue(x);
   cout << x;    // 5

In call by value, when you pass a value to printvalue(x) i.e. the argument which is 5, it is copied to void printvalue(int x). Now, we have two different values 5 and the copied value 5 and these two values are stored in different memory locations. So if you make any change inside void printvalue(int x) it won't reflect back to the argument.

2. Pass By Reference/ Call By Reference

   void printvalue(int &x) 
   {
      x = x + 1 ;
      cout << x ; // 6
   }

   int x = 5;
   printvalue(x);
   cout << x;   // 6

In call by reference, there's only one difference. We use & i.e. the address operator. By doing
void printvalue(int &x) we are referring to the address of x which tells us that it both refers to the same location. Hence, any changes made inside the function will reflect outside.

Now that you're here, you should also know about ...

3. Pass By Pointer/ Call By Address

   void printvalue(int* x) 
   {
      *x = *x + 1 ;
      cout << *x ; // 6
   }

   int x = 5;
   printvalue(&x);
   cout << x;   // 6

In pass by address, the pointer int* x holds the address passed to it printvalue(&x). Hence, any changes done inside the function will reflect outside.

迷爱 2024-07-17 15:21:35

按值传递意味着如何通过使用参数将值传递给函数。 在按值传递中,我们复制存储在我们指定的变量中的数据,并且它比按引用传递慢,因为数据是复制的。

或者我们对复制的数据进行更改。 原始数据不受影响。 在通过引用传递或通过地址传递时,我们发送到变量本身的直接链接。 或者传递一个指向变量的指针。 它更快,因为消耗的时间更少。

Pass by value means how to pass a value to a function by making use of arguments. In pass by value, we copy the data stored in the variable we specify, and it is slower than pass by reference because the data is copied.

Or we make changes in the copied data. The original data is not affected. And in pass by reference or pass by address, we send a direct link to the variable itself. Or passing a pointer to a variable. It is faster because less time is consumed.

你的背包 2024-07-17 15:21:35

下面是一个示例,演示了按值传递 - 指针值 - 引用之间的差异:

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

“按引用传递”方法有一个重要的限制。 如果参数被声明为通过引用传递(因此其前面带有 & 符号),则其对应的实际参数必须是变量

指“按值传递”形式参数的实际参数一般可以是表达式,因此不仅可以使用变量,还可以使用文字,甚至可以使用函数调用的结果。

该函数无法将值放置在变量以外的位置。 它不能为文字分配新值或强制表达式更改其结果。

PS:您还可以在当前线程中查看 Dylan Beattie 的答案,其中用简单的文字进行了解释。

Here is an example that demonstrates the differences between pass by value - pointer value - reference:

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

The “passing by reference” method has an important limitation. If a parameter is declared as passed by reference (so it is preceded by the & sign) its corresponding actual parameter must be a variable.

An actual parameter referring to “passed by value” formal parameter may be an expression in general, so it is allowed to use not only a variable but also a literal or even a function invocation's result.

The function is not able to place a value in something other than a variable. It cannot assign a new value to a literal or force an expression to change its result.

PS: You can also check Dylan Beattie answer in the current thread that explains it in plain words.

当爱已成负担 2024-07-17 15:21:35

问题是“对”。

没有人指出重要的一点。 在传递值时,会占用额外的内存来存储传递的变量值。

在传递引用时,值不会占用额外的内存(在某些情况下内存效率很高)。

The question is "vs".

And nobody has pointed to an important point. In passing with values, additional memory is occupied to store the passed variable values.

While in passing with a reference, no additional memory is occupied for the values (memory efficient in circumstances).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文