考虑以下代码:
#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工作。为什么呢?
答案 0 :(得分:1)
按值传递意味着该函数将创建参数的副本,因此它不会更改通过引用传递时传递的实际参数将允许您修改函数内的参数。了解这一点,这就是Node* &head
可行的原因,但Node* head
不适用于您的insertAtHead()
功能。
对于append()函数,它被修改,因为Node* ptr
变量指向h1的地址,这使得您可以将下一个值设置为h2(参数设置为n3),而无需设置要通过引用传递的参数。在n1到达append函数之前,它将下一个节点设置为NULL,而在调用append函数之后,它现在将下一个节点设置为n3。 (IMO我认为你应该给他们不同的名字,看到n1,h1,n2,h2等会让人感到困惑......)
答案 1 :(得分:1)
原因很简单:
在void insertAtHead(Node * head,int k)的情况下,当您按值传递时,您正在处理堆栈上存在的实际头部副本,因此在退出函数时它将被丢弃
这就是你必须通过引用传递的原因,因此head不是堆栈上节点的副本,而是你想要操作的实际节点。
编辑以回答您的评论:
追加是有效的,因为你得到了一个指针的副本,所以你可以修改它指向的相邻数据,但你不能改变你得到的实际指针,因为它是一个生活在堆栈上的瞬态副本。
答案 2 :(得分:1)
在函数追加指针h1未更改
在函数insertAtHead head应该被更改,但不是更改本地变量。您应该考虑参数是函数的局部变量。因此在退出函数后,所有局部变量都将被销毁。
如果函数追加处理空列表,则可以获得相同的效果。在这种情况下,你必须在函数内部分配头部,这些头部的变化不会影响原始头部。
正确编写这些函数有三种方法。每个函数都返回更新的头部,或者必须通过引用或通过指向头部间接传递头部。
例如,考虑如果列表为空,即当第一个参数等于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才会被改变,之后退出该函数就会被破坏。原始变量头将保持与以前相同的旧值。
答案 3 :(得分:0)
指针按值传递(您传递指针值的副本 - 即指向的内存地址的副本)。
通过引用传递引用(即,当函数参数在函数签名中具有&amp;前缀时)。在这种情况下,如果您将参考点指向函数内的其他内容,则当程序流退出函数作用域并返回时,原始引用也指向另一个参考。
以下是Java的不同之处(使用C ++作为能够通过Java无法通过引用传递的示例):
http://blog.aaronshaw.net/2014/02/13/java-is-always-pass-by-value/