繁体   English   中英

指针引用在 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 ,它的工作原理是一样的 -

与 Node* 和 Node* & 相同的功能

所以,

引用和指针都包含变量/内存的地址。 引用具有变量的语义(如果设置值,则将数据写入引用的内存),指针具有指针的语义(如果设置值,则指针内存不会更改)并且您可以设置指向其他内存的指针.

关于 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 中指针的用法非常相似,除了以下几点:

  1. 引用保存单个值而不是数组
  2. 引用是非空的(这并不总是可取的)

这意味着您可以(或应该)在您想要更改原始传递变量(而不是它的副本)时传递引用。

也就是说,与您的函数等效的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM