C++通过Refence或Value传递指针

C++ passing pointers by Refrence or Value

本文关键字:指针 Value 通过 Refence C++      更新时间:2023-10-16

考虑以下代码:

#include <iostream>
using namespace std;
struct Node
{
int x;
Node *next;
};
void append(Node* h1, Node* h2)
{
cout << h1 << ":" << h2;
cout << endl;
Node *ptr;
ptr = h1;
while ( ptr->next != NULL)
{
ptr = ptr->next;
}
ptr->next = h2;
}
void insertAtHead(Node* head, int k)
{
Node *tmp = new Node;
tmp -> x = k;
tmp -> next = head;
head = tmp; 
}
int main()
{
Node *n1  = new Node;
n1->x = 1;
n1->next = NULL; 

Node *n2  = new Node;
n2->x = 2;
n2->next = NULL;
Node *n3  = new Node;
n3->x = 3;
n3->next = n2;
cout << n1 << ":" << n3;
cout << endl;
append(n1,n3);
insertAtHead(n1,4);
while(n1 != NULL)
{
cout << n1->x;
n1 = n1->next;
}
cout << endl;
}

即使我们有Node* h1,append函数也能工作,但即使我们有相同的Node* head.,insertAtHead也不能工作。为什么??

我们需要Node* &head才能使appendAtHead工作。为什么?

函数中追加指针h1未更改

在函数insertAtHead头应该改变,但局部变量却改变了。您应该考虑到参数是函数的局部变量。因此,在退出函数后,所有的局部变量都会被销毁。

如果函数append处理一个空列表,也可以得到同样的效果。在这种情况下,您必须在函数内部分配头部,头部的这些变化不会影响原始头部。

有三种方法可以正确地编写这些函数。每个函数都返回更新后的头,或者必须通过引用或通过指向头的指针间接传递头。

例如,考虑如果列表为空,即当第一个参数等于NULL时,函数append将如何工作。

void append(Node* h1, Node* h2)
{
cout << h1 << ":" << h2;
cout << endl;
if ( h1 == NULL ) 
{
h1 = h2;
}
else
{
Node *ptr = h1;
while ( ptr->next != NULL) ptr = ptr->next; 
ptr->next = h2;
}
}

正如您在本例中看到的,它将具有与函数insertAtHead相同的错误。函数内部h1的变化不会影响head的原始值。只有局部变量h1会被更改,然后在退出函数后被销毁。原始可变水头将保持与以前相同的旧值。

通过值传递意味着函数将创建参数的副本,这样它就不会更改在通过引用传递时传递的实际参数。这将允许您修改函数中的参数。知道这一点,这就是为什么Node* &head可以工作,但Node* head不能用于insertAtHead()功能的原因。

对于append()函数,它被修改是因为Node* ptr变量指向h1的地址,这使您可以将下一个值设置为h2(参数设置为n3),而不必将参数设置为通过引用传递。在n1到达append函数之前,它将下一个节点设置为NULL,而在调用append函数之后,它现在将下一节点设置为n3。(IMO我认为你应该给它们起不同的名字,看到n1、h1、n2、h2等会有点困惑…)

原因很简单:

在void insertAtHead(Node*head,int k)的情况下,当您按值传递head时,您正在处理实际head的副本,该副本存在于堆栈中,因此在退出函数时会被丢弃。

这就是为什么必须通过引用传递,所以head不是堆栈上节点的副本,而是要操作的实际节点。

编辑以回答您的评论:

append之所以有效,是因为您正在获取指针的副本,因此您可以修改它所指向的下层数据,但不能更改您所获得的实际指针,因为它是堆栈上的临时副本。

指针按值传递(您传递指针值的副本,即指向的内存地址的副本)。

引用是通过引用传递的(即,当函数参数在函数签名中具有&前缀时)。在这种情况下,如果您使引用指向函数内的其他对象,那么当程序流退出函数范围并返回时,原始引用也指向其他对象。

以下是Java的差异说明(使用C++作为能够通过引用传递的示例,而Java无法做到这一点):

http://blog.aaronshaw.net/2014/02/13/java-is-always-pass-by-value/