c++ - 在C和C++中 “pass by reference”之间到底有什么区别?

标签 c++ c terminology pass-by-reference pass-by-value

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

最佳答案

有些问题已经解决了the difference between passing by reference and passing by value。从本质上讲,按值将参数传递给函数意味着函数将拥有其自己的参数副本-将其值复制。修改该副本不会修改原始对象。但是,通过引用传递时,函数内部的参数引用的是传入的同一对象-函数内部的任何更改都可以在外部看到。

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

C

在C语言中,技术意义上的一切都是通过值(value)传递的。也就是说,无论您将什么作为函数的参数,它都会被复制到该函数中。例如,使用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
}
x的值被复制到foo中,并且该副本递增。 x中的main继续具有其原始值。

如您所知,对象可以是指针类型。例如,int* pp定义为指向int的指针。请务必注意,以下代码引入了两个对象:
int x = 5;
int* p = &x;

第一个是int类型,值5。第二个是int*类型,其值是第一个对象的地址。

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

由于具有相同值的两个指针总是指向相同的对象(它们包含相同的地址),因此所指向的对象可以通过两者来访问和修改。这给出了通过引用传递指向对象的语义,尽管实际上实际上不存在引用-C语言中根本没有引用。请看一下经过更改的示例:
void foo(int* param) { (*param)++; }

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

我们可以说在将int*传递给函数时,它指向的int是“通过引用传递”的,但实际上int从未真正传递给任何地方-仅将指针复制到函数中。这给我们提供了“按值传递”和“按引用传递”的口语含义。

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

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



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

C++

C++从C采用了许多其原始语言功能。其中包括指针,因此这种“按引用传递”的通俗形式仍然可以使用-*p仍在取消引用p。但是,使用该术语会造成混淆,因为C++引入了C所没有的功能:真正传递引用的能力。

后面跟有“&”号的类型是引用类型2。例如,int&是对int的引用。当将参数传递给采用引用类型的函数时,该对象实际上是通过引用传递的。没有指针,没有对象的复制,没有任何东西。函数内部的名称实际上指的是与传入的对象完全相同的对象。与上面的示例形成对比:
void foo(int& param) { param++; }

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

现在,foo函数具有一个对int的引用的参数。现在,当传递x时,param指向完全相同的对象。递增param可以使x的值发生明显变化,现在x的值可以为6。

在此示例中,没有任何值传递。没有复制任何内容。与C中通过引用传递实际上只是按值传递指针不同,在C++中,我们可以真正地按引用传递。

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

其他语言

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

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

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

1所谓“口语化”,并不是要暗示“通过引用传递”的C含义不如C++含义真实,只是C++确实具有引用类型,因此您真正是通过引用传递。 C的含义是对值(value)传递的抽象。

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

关于c++ - 在C和C++中 “pass by reference”之间到底有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13654138/

相关文章:

c++ - 如何将从文件读取的字符串关联到枚举值?

c++ - 运行时固定大小的 std::vector?

c++ - 迭代器和反向迭代器的区别

c - 该指针函数中返回代码的用途是什么?

c - 3x3 数组 = 10 个数字

function - 是否有一个术语来形容一个函数,其唯一目的是忽略一个或多个参数?

php - 通俗地说什么是位域?

c++ - Cout 里面 while?

c - 与 fork 子共享内存

operators - 三元和三元的区别?