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

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

1
如果您不知道地址是什么,请参见这里 - mfaani
18个回答

5
它们之间的一个主要区别是值类型变量存储值,因此在方法调用中指定值类型变量会将该变量值的副本传递给方法。引用类型变量存储对对象的引用,因此在参数中指定引用类型变量会将实际引用的副本传递给方法。即使引用本身是按值传递的,方法仍然可以使用接收到的引用与原始对象进行交互,并可能修改它。同样,通过返回语句从方法返回信息时,方法会返回存储在值类型变量中的值的副本或存储在引用类型变量中的引用的副本。当返回引用时,调用方法可以使用该引用与所引用的对象进行交互。因此,实际上,对象总是按引用传递的。
在 C# 中,为了按引用传递变量以便被调用方法修改变量的值,C# 提供了 ref 和 out 关键字。将 ref 关键字应用于参数声明可以使您通过引用将变量传递给方法——被调用的方法将能够修改调用者中的原始变量。ref 关键字用于已在调用方法中初始化的变量。通常,当方法调用包含未初始化的变量作为参数时,编译器会生成错误。在参数前加关键字 out 创建输出参数。这告诉编译器参数将通过引用传递到被调用的方法中,并且被调用的方法将为调用者中的原始变量分配一个值。如果该方法未在执行的每个可能路径中为输出参数分配值,则编译器会生成错误。这也防止编译器为传递给方法作为参数的未初始化变量生成错误消息。通过指定多个输出(ref 和/或 out)参数,方法只能通过返回语句向其调用者返回一个值,但可以返回多个值。
请参见此处的 C# 讨论和示例 链接文本

3
如果您在将变量传递到函数后不想更改原始变量的值,则应使用“按值传递”参数构建函数。然后该函数将仅具有值,而不具有传入变量的地址。没有变量的地址,函数内部的代码就无法从外部更改变量的值。
但是,如果您希望函数具有从外部看更改变量值的能力,则需要使用“按引用传递”。因为该函数同时传递了值和地址(引用),并且这些都在函数内部可用。

3

示例:

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

通常最好使用const &。这样就不会产生构造和销毁的开销。如果引用不是const,则表明您的接口将更改传入的数据。

2
简而言之,“按值传递”是指它的值是什么,“按引用传递”是指它在哪里。
如果你的值是VAR1 =“Happy Guy!”,你只会看到“Happy Guy!”。如果VAR1改变为“Happy Gal!”,你就不会知道。如果它是通过引用传递的,并且VAR1发生了变化,那么你就会知道。

1

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
通过值传递是指如何利用参数将值传递给函数。在通过值传递时,我们复制存储在指定变量中的数据,这比通过引用传递慢,因为数据被复制了。
或者我们对复制的数据进行更改。原始数据不受影响。在通过引用或通过地址传递时,我们直接发送到变量本身的链接。或传递一个指向变量的指针。这样做速度更快,因为消耗的时间较少。

0

这里有一个示例,演示了按值传递 - 指针值 - 引用之间的区别:

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在当前线程中的答案,他用简单的语言解释了这个问题。


你说“如果一个参数被声明为引用,那么相应的实际参数必须是一个变量”,但这并不总是正确的。如果一个引用被绑定到一个临时对象(比如函数的返回值),它的生命周期会被延长以匹配该引用。详情请参见这里 - Chris Hunt

0

这个问题是“vs”。

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

而在传递引用时,不需要为值占用额外的内存(在某些情况下具有内存效率)。


但是只需要(在堆栈上)临时的额外内存? - Peter Mortensen
“vs”在这个上下文中是什么意思?你能详细说明一下吗? - Peter Mortensen
@PeterMortensen,“vs”可以理解为“对比”或“并列”。 - sifr_dot_in
@PeterMortensen 如果你说“但只是暂时的...”,那么“额外的内存”这一点是可以预料到的。 - sifr_dot_in

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