C 和 C++ 中的“通过引用传递”到底有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13654138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:35:31  来源:igfitidea点击:

What exactly is the difference between "pass by reference" in C and in C++?

c++cterminologypass-by-referencepass-by-value

提问by Joseph Mansfield

The phrase "pass by reference" is used by C and C++ developers alike but they appear to be used to mean different things. What exactly is the difference between this equivocal phrase in each language?

C 和 C++ 开发人员都使用短语“通过引用传递”,但它们似乎用于表示不同的事物。每种语言中这个模棱两可的短语之间到底有什么区别?

回答by Joseph Mansfield

There are questions that already deal with the difference between passing by reference and passing by value. In essence, passing an argument by value to a function means that the function will have its own copy of the argument - its valueis copied. Modifying that copy will not modify the original object. However, when passing by reference, the parameter inside the function refers tothe same object that was passed in - any changes inside the function will be seen outside.

有一些问题已经处理了按引用传递和按值传递之间的区别。本质上,将参数按值传递给函数意味着该函数将拥有自己的参数副本 - 它的被复制。修改该副本不会修改原始对象。然而,通过引用传递时,在该函数内的参数是指中传递相同的对象-的任何改变内部的功能可以看到外面。

Unfortunately, there are two ways in which the phrases "pass by value" and "pass by reference" are used which can cause confusion. I believe this is partly why pointers and references can be difficult for new C++ programmers to adopt, especially when they've come from a background in C.

不幸的是,有两种使用短语“按值传递”和“按引用传递”的方式可能会引起混淆。我相信这就是为什么新的 C++ 程序员难以采用指针和引用的部分原因,尤其是当他们来自 C 背景时。

C

C

In C, everything is passed by value in the technical sense. That is, whatever you give as an argument to a function, it will be copied into that function. For example, calling a function void foo(int)with foo(x)copies the value of xas the parameter of foo. This can be seen in a simple example:

在 C 中,一切都在技术意义上按值传递。也就是说,无论您将什么作为参数提供给函数,它都会被复制到该函数中。例如,调用一个函数void foo(int)foo(x)复制 的值x作为 的参数foo。这可以在一个简单的例子中看到:

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

int main()
{
  int x = 5;
  foo(x);
  printf("%d\n",x); // x == 5
}

The value of xis copied into fooand that copy is incremented. The xin maincontinues to have its original value.

的值x被复制到foo并递增该副本。将xmain继续拥有其原始值。

As I'm sure you're aware, objects can be of pointer type. For example, int* pdefines pas a pointer to an int. It is important to note that the following code introduces two objects:

我相信您知道,对象可以是指针类型。例如,int* p定义p为指向int. 需要注意的是,下面的代码引入了两个对象:

int x = 5;
int* p = &x;

The first is of type intand has the value 5. The second is of type int*and its value is the address of the first object.

第一个是 typeint并且值为5。第二个是类型int*,它的值是第一个对象的地址。

When passing a pointer to a function, you are still passing it by value. The address it contains is copied into the function. Modifying that pointerinside the function will not change the pointer outside the function - however, modifying the object it points towill change the object outside the function. But why?

将指针传递给函数时,您仍然是按值传递它。它包含的地址被复制到函数中。修改函数内部的指针不会改变函数外部的指针——但是,修改它指向的对象会改变函数外部的对象。但为什么?

As two pointers that have the same value always point at the same object (they contain the same address), the object that is being pointed to may be accessed and modified through both. This gives the semantics of having passed the pointed to object by reference, although no references ever actually existed - there simply are no references in C. Take a look at the changed example:

由于具有相同值的两个指针始终指向同一个对象(它们包含相同的地址),因此可以通过两者访问和修改所指向的对象。这给出了通过引用传递指向对象的语义,尽管实际上没有引用存在 - 在 C 中根本没有引用。看看改变的例子:

void foo(int* param) { (*param)++; }

int main()
{
  int x = 5;
  foo(&x);
  printf("%d\n",x); // x == 6
}

We can say when passing the int*into a function, that the intit points to was "passed by reference" but in truth the intwas never actually passed anywhere at all - only the pointer was copied into the function. This gives us the colloquial1meaning of "pass by value" and "pass by reference".

我们可以说,当将 传递给int*一个函数时,int它指向的是“通过引用传递”,但实际上它int根本没有真正传递到任何地方——只有指针被复制到函数中。这给了我们“按值传递”和“按引用传递”的口语1含义。

The usage of this terminology is backed up by terms within the standard. When you have a pointer type, the type that it is pointing to is known as its referenced type. That is, the referenced type of int*is int.

该术语的使用由标准中的术语支持。当您有一个指针类型时,它所指向的类型称为它的引用类型。也就是说, 的引用类型int*int

A pointer typemay be derived from a function type, an object type, or an incomplete type, called the referenced type.

指针类型可以从函数类型,对象类型,或不完整的类型,被称为导出引用的类型

While the unary *operator (as in *p) is known as indirection in the standard, it is commonly also known as dereferencing a pointer. This further promotes the notion of "passing by reference" in C.

虽然一元运算*符(如 in *p)在标准中称为间接,但它通常也称为取消引用指针。这进一步促进了 C 中“通过引用传递”的概念。

C++

C++

C++ adopted many of its original language features from C. Among them are pointers and so this colloquial form of "passing by reference" can still be used - *pis still dereferencing p. However, using the term will be confusing, because C++ introduces a feature that C doesn't have: the ability to truly pass references.

C++ 从 C 中采用了许多其原始语言特性。其中包括指针,因此这种“通过引用传递”的口语形式仍然可以使用 -*p仍然是解引用p。但是,使用该术语会令人困惑,因为 C++ 引入了 C 没有的特性:真正传递引用的能力。

A type followed by an ampersand is a reference type2. For example, int&is a reference to an int. when passing an argument to a function that takes reference type, the object is truly passed by reference. There are no pointers involved, no copying of objects, no nothing. The name inside the function actually refers to exactly the same object that was passed in. To contrast with the example above:

后跟与符号的类型引用类型2。例如,int&是对int. 当将参数传递给采用引用类型的函数时,对象实际上是通过引用传递的。不涉及指针,不复制对象,什么都没有。函数内部的名称实际上指的是传入的完全相同的对象。 与上面的示例形成对比:

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

int main()
{
  int x = 5;
  foo(x);
  std::cout << x << std::endl; // x == 6
}

Now the foofunction has a parameter that is a reference to an int. Now when passing x, paramrefers to precisely the same object. Incrementing paramhas a visible change on the value of xand now xhas the value 6.

现在该foo函数有一个参数,该参数是对int. 现在,当传递x,param指的是完全相同的对象。递增param对 的值有明显的变化x,现在x值为 6。

In this example, nothing was passed by value. Nothing was copied. Unlike in C, where passing by reference was really just passing a pointer by value, in C++ we can genuinely pass by reference.

在这个例子中,没有任何东西是按值传递的。什么都没有被复制。与在 C 中按引用传递实际上只是按值传递指针不同,在 C++ 中我们可以真正按引用传递。

Because of this potential ambiguity in the term "pass by reference", it's best to only use it in the context of C++ when you are using a reference type. If you are passing a pointer, you are not passing by reference, you are passing a pointer by value (that is, of course, unless you are passing a reference to a pointer! e.g. int*&). You may, however, come across uses of "pass by reference" when pointers are being used, but now at least you know what is really happening.

由于术语“通过引用传递”中的这种潜在歧义,当您使用引用类型时,最好仅在 C++ 的上下文中使用它。如果您传递的是指针,则不是通过引用传递,而是通过值传递指针(当然,除非您传递的是对指针的引用!例如int*&)。然而,当使用指针时,您可能会遇到“通过引用传递”的用法,但现在至少您知道真正发生了什么。



Other languages

其他语言

Other programming languages further complicate things. In some, such as Java, every variable you have is known as a reference to an object (not the same as a reference in C++, more like a pointer), but those references are passed by value. So even though you appear to be passing to a function by reference, what you're actually doing is copying a reference into the function by value. This subtle difference to passing by reference in C++ is noticed when you assign a new object to the reference passed in:

其他编程语言使事情进一步复杂化。在某些语言中,例如 Java,您拥有的每个变量都被称为对对象的引用(与 C++ 中的引用不同,更像是指针),但这些引用是按值传递的。因此,即使您看起来是通过引用传递给函数,实际上您所做的是通过值将引用复制到函数中。当您将新对象分配给传入的引用时,会注意到 C++ 中按引用传递的这种细微差别:

public void foo(Bar param) {
  param.something();
  param = new Bar();
}

If you were to call this function in Java, passing in some object of type Bar, the call to param.something()would be called on the same object you passed in. This is because you passed in a reference to your object. However, even though a new Baris assigned to param, the object outside the function is still the same old object. The new one is never seen from the outside. That's because the reference inside foois being reassigned to a new object. This kind of reassigning references is impossible with C++ references.

如果您要在 Java 中调用此函数,传入某个类型的对象,则将在您传入的同一对象上Bar调用param.something()。这是因为您传入了对对象的引用。然而,即使Bar为 分配了一个新param对象,函数外的对象仍然是同一个旧对象。从外面看不到新的。那是因为内部的引用foo被重新分配给一个新对象。对于 C++ 引用,这种重新分配引用是不可能的。



1By "colloquial", I don't mean to suggest that the C meaning of "pass by reference" is any less truthful than the C++ meaning, just that C++ really does have reference types and so you are genuinely passing by reference. The C meaning is an abstraction over what is really passing by value.

1通过“口语化”,我并不是要暗示“通过引用传递”的 C 含义比 C++ 含义更不真实,只是 C++ 确实具有引用类型,因此您确实是通过引用传递。C 的含义是对真正按值传递的内容的抽象。

2Of course, these are lvalue references and we now have rvalue references too in C++11.

2当然,这些是左值引用,我们现在在 C++11 中也有右值引用。