C++通过Refence或Value传递指针
C++ passing pointers by Refrence or Value
考虑以下代码:
#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/
- 1d 智能指针不适用于语法 (*)++
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 为什么使用 "this" 指针调用派生成员函数?
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用指针从C++中的数组中获取最大值
- 助记符和指向成员语法的指针
- 嵌入方指针压缩已禁用
- 数组的指针从不分段故障
- C++ 指针的内存地址和指向数组的内存地址如何相同?
- 何时在引用或唯一指针上使用移动语义
- QMetaObject invokeMethod的基于函数指针的语法
- 如何从 std::atomic 中提取指针 T<T>?
- 如何在 C# 中映射双 C 结构指针?
- C++将浮点指针值舍入为小数位数
- 尝试分配函数指针时获取"Void value not ignored as it ought to be"
- 指针数组" Use of uninitialised value"错误
- C++通过Refence或Value传递指针
- 在递归函数中创建 std::list of value 而不是 std::list of 指针
- <Value> 使用指向值的原始指针从 std::list 中删除?
- new(size, value) Type[0]返回的指针是否合法,是否可以用来构建数组?