为什么在构建链表C++时必须指向指针

Why do I have to point to a pointer when building a linked list C++

本文关键字:指针 构建 链表 C++ 为什么      更新时间:2023-10-16

构建链表结构时。我已经学会了正确的方法,但我不知道为什么更简单的方法不起作用。我搜索了互联网,买了一本书,我玩了代码并进行了理论化,我终于宣布退出,如果我只是错过了什么,我愿意在我的问题上处理一些负面问题。所以这里是:

#include <bits/stdc++.h>
using namespace std;

struct Node{
    int data;
    Node* next;
    // Constructor
    Node(int data)
    {
       this->data = data;
       next = NULL;
    }
};
// Class to represent Red-Black Tree
class LinkedList{
private:
    Node* root;
public:
    // Constructor
    LinkedList() { root = NULL; }
    //insert new value into list
    void insert(const int n);
    //print all values from root to end;
    void print();
};
// Function to insert a new node with given data
void LinkedList::insert(const int data){
    Node** pp = &root;
    while (*pp)
        pp = &((*pp)->next);
    *pp = new Node(data);
}
void LinkedList::print(){
    Node** pp = &root;
    while (*pp){
        cout << (*pp)->data << " ";
        pp = &((*pp)->next);
    }
    cout << endl;
}

int main(){
    LinkedList ll;
    ll.insert(5);
    ll.insert(2);
    ll.insert(22);
    ll.print();
    return 0;
}

非常简单的东西,它运行得很好。但是,这是一个问题。因为我不知道为什么我不能这样做:

void LinkedList::insert(const int data){
    Node* pp = root;
    while (pp)
        pp = pp->next;
    pp = new Node(data);
}

它应该像我在脑海中看到指针一样做同样的事情。当我在纸上写下地址/内容对时,它仍然有意义。

谢谢-康纳。

void LinkedList::insert(const int data){
    Node* pp = root;
    while (pp)
        pp = pp->next;
    pp = new Node(data);
}

此代码实际上不执行任何操作。将pp设置为特定值会很麻烦,只是忽略该值并重新分配pp其他值。然后它返回,pp超出范围,它分配给它的值将永远丢失。

让我们逐行浏览一下:

void LinkedList::insert(const int data){
    Node* pp = root;

在这一点上,pp是一个局部变量,等于root

while (pp)
    pp = pp->next;

这将更改 pp 的值,直到它从列表末尾运行。因此,我们浏览了整个列表,没有做任何其他事情,只是最终突破了pp设置为NULL

    pp = new Node(data);

不,我们创建一个新Node并更改pp的值以指向该新Node。所以我们不厌其烦地使pp指向最后一个节点,只经过它,然后将其更改为指向新节点。

}

现在我们回来了,pp超出了范围,我们也没有对新Node做任何事情。

您还可以看到:

void LinkedList::insert(const int data){
    Node* pp = root;
    while (pp)
        pp = pp->next;
    pp = new Node(data);
}

等效于(假设它不会永远运行(:

void LinkedList::insert(const int data){
    Node* pp = root;
    pp = NULL;
    pp = new Node(data);
}

相当于:

void LinkedList::insert(const int data){
    Node* pp = NULL;
    pp = new Node(data);
}

相当于:

void LinkedList::insert(const int data){
    Node* pp = new Node(data);
}

相当于:

void LinkedList::insert(const int data){
    new Node(data);
}

这显然创造了一个新的Node并迅速泄漏它。

我也不喜欢这种方式:

void LinkedList::insert(const int data){
    Node** pp = &root;
    while (*pp)
        pp = &((*pp)->next);
    *pp = new Node(data);
}

我会使用的是:

// returns the last node or NULL on failure
bool LinkedList::insert(const int data){
    Node* pp = root;
    if (!pp){ // if it is an empty list
        root = new Node(data); // assign it to something
        return root; // convert to bool and return - false if new failed
    }
    while (pp->next) // check if we have a next node
        pp = pp->next; // we do have a next node so go there
    // at this point pp->next will be NULL
    pp->next = new Node(data); // assign it to something
    return pp->next; // convert to bool and return - false if new failed
}

认为指针只是存储内存地址的变量类型。例如,int* 只是一个变量("一个盒子"(,用于存储另一个 int 变量的地址。

当您在"插入"功能中执行此操作时:

Node* pp;

pp 是函数的局部变量,用于存储 Node 对象的地址。从函数返回时,局部变量将被销毁。

当您执行以下操作时:

Node ** pp;

定义一个局部变量,该变量也存储地址,但不存储到 Node 对象,而是存储指向 Node 对象的指针,即另一个存储 Node 对象的地址的"中间框"。

最后,当您这样做时:

pp = &((*pp)->next);

你的局部变量,当你的函数返回时,它将被销毁,存储到"下一个"成员的地址。不像你这样做的时候:

pp = pp->next

您为"pp"分配存储在"next"中的相同地址(如果 next 是0x1234,这是一个表示对象地址的简单值,pp 将获得相同的值 0x1234(,该地址在列表的最后一个对象中将为 NULL。

用这句话:

*pp = new Node(data);
"pp"的内容是另一个指向Node对象(而不是直接对象(的指针的地址,在这种情况下,这是"next"指针的地址(不是"next"指针

的值(,当你将"new Node(data("分配给pp的内容时,你正在将对象地址分配给"next"指针, 它是列表对象的成员,而不是局部变量,您得到的是将新节点链接到列表。

为了更好地理解指针,您可以在论文中绘制变量的结构,将指针绘制为存储地址的框,并具有自己的地址作为变量。