![](/img/trans.png)
[英]Why do we have different effect when setting pointer=nullpointer and pointer->next=nullpointer in linked list - C++
[英]How does reference to pointer exactly work in C++, and when do we need them (in the case of linked list)
我知道指针保存变量的地址。 并且引用指向符号表中的相同地址(即变量的相同地址,它们被分配到)。
我的问题是,对指针的引用究竟是如何工作的。 我们什么时候需要它们,而不是单独使用指针(而不是使用对指针的引用)。 如果您能向我解释对单向链表的引用的使用,将会很有帮助。
我有以下代码使用函数删除链表的头指针:
struct Node
{
int data;
Node* next;
};
struct Node* newNode(int data)
{
Node* temp = new Node;
temp->data = data;
temp->next = nullptr;
return temp;
}
Node* deleteHead(Node* &head)
{
if (head)
{
Node* temp = head;
head = head->next;
delete temp;
}
return head;
}
int main()
{
Node* head = newNode(1);
head->next = newNode(6);
head->next->next = newNode(4);
head->next->next->next = newNode(8);
head = deleteHead(head);
Node* temp = head;
while (temp != nullptr)
{
cout << temp->data << " " << endl;
temp = temp->next;
}
return 0;
}
在deleteHead(Node* &head)
函数中,该函数采用参数Node* &head
。 但是,即使参数是Node* head
,代码也能正常工作。 在什么情况下我们需要在链表中传递Node* &
而不是Node*
?
下面是上面的deleteHead(Node* &head)
函数,如果我们只使用Node* head
作为参数,而不是Node* &head
,它的工作原理是一样的 -
所以,
引用和指针都包含变量/内存的地址。 引用具有变量的语义(如果设置值,则将数据写入引用的内存),指针具有指针的语义(如果设置值,则指针内存不会更改)并且您可以设置指向其他内存的指针.
关于 deleteHead(Node* &head) - 您使用包含节点指针的真实变量的引用。 该函数在同一变量中返回 head 的新值,也作为返回值。
您通过引用传递指针的原因与通过引用传递非指针的原因相同:让函数修改其值。
让我用一个更简单的例子
#include <iostream>
void foo(int*& x) {
*x = 42; // change the value of the int x points to
x = nullptr; // change the value of x
}
第一行修改x
指向的值(但不修改x
)。 第二行修改x
本身。
int main() {
int y = 42;
int* y_ptr = &y;
foo(y_ptr);
if (y_ptr == &y) std::cout << "cannot happen";
}
因为我们设置了x = nullptr
,调用后y_ptr
将不再指向y
。
现在,如果我们修改foo
以不引用引用,我们将得到:
#include <iostream>
void foo(int* x) {
*x = 42; // change the value of the int x points to
x = nullptr; // change the value of x
}
第一行再次修改x
指向的int
。 但是,现在第二行只对x
局部的x
有影响。
int main() {
int y = 42;
int* y_ptr = &y;
foo(y_ptr);
if (y_ptr == nullptr) std::cout << "cannot happen";
}
y_ptr
的值y_ptr
将其传递给foo
来更改,因为它是按值传递的。
在您的代码中,您有
Node* deleteHead(Node* &head)
{
if (head)
{
Node* temp = head;
head = head->next;
delete temp;
}
return head;
}
当你写head = deleteNode(head)
会发生两件事:
head
(因为它是通过引用传递的)以指向head->next
。head->next
)并分配给head
。 所以你基本上指定了两次head
。 因为head
是通过引用传递的, deleteNode
会在不使用返回值的情况下做正确的事情:
deleteNode(head); // this already does modify head
...或者反过来说:如果您从功能返回“新”头( head->next
)并将其分配给head
,那么是否通过引用传递指针并不重要,因为分配已完成函数内部也有同样的效果。
您的代码类似于
int* bar(int*& x) {
x = nullptr;
return x;
}
然后通过调用它
int y = 42;
int* y_ptr = &y;
y_ptr = bar(y_ptr);
通过不使用返回值bar(y_ptr)
可以实现相同的效果。 或者没有指针也一样(因为指针在这里真的没有区别):
int moo(int& x) {
x = 0;
return x;
}
int x = 42;
x = moo(x); // same as `moo(x)`
PS:你不需要两者(返回指针并在函数中分配它),所以最好让函数返回void
。
引用是与值语义一起使用的“安全指针”(这在运算符重载上下文中非常有用),因此引用的用法与 C 中指针的用法非常相似,除了以下几点:
这意味着您可以(或应该)在您想要更改原始传递变量(而不是它的副本)时传递引用。
也就是说,与您的函数等效的 C(粗略)是Node* deleteHead(Node** head)
。 请注意,由于您传递了一个引用,原始head
变量被修改,因此您的函数变得有点奇怪,因为它既修改 head 又返回其值。 您可以使用以下选项之一:
(1) 删除 head(如果列表大小非空)并返回指向下一个元素的指针,这是不可取的,因为它会将 head 作为悬空指针留下。 这是您的原始功能,但没有收到参考。
Node* deleteHead(Node* head)
{
if (head)
{
Node* temp = head; // You might want to use auto
head = head->next;
delete head;
}
return head;
}
(2) 和你的函数一样,但是没有返回值(因为你已经修改了 head)。 如果不传递引用,此方法将无法工作。
void deleteHead(Node* &head)
{
if (head)
{
Node* temp = head->next;
delete head; // deletes the content of head
head = temp;
}
}
对于指针 deleteHead 函数的引用,您可以使用 head = deleteHead(head) 或 deleteHead(head) 因为 head 只是 main 函数中 head 的引用,deleteHead 函数中 head 上的任何更改实际上都应用于 head 变量中主要功能。 对于指针版本,您必须使用 head = deleteHead(head)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.