簡體   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