为什么链表的第一个节点声明为指针?

Why the first node of a linked list is declared as a pointer?

现在我知道为什么在定义链表时使用指针了。仅仅是因为结构不能有递归定义,如果没有指针,编译器就不会能够计算节点结构的大小。

struct list{
        int data;
        struct list* next;    // this is fine
};

但是当我将链表的第一个节点声明为:

struct list* head;

为什么这必须是一个指针?不能简单地声明为

struct list head;

以及用于进一步用途的地址?请澄清我的疑问。

你可以这样声明一个列表

struct list head = {};

但是实现访问列表的功能会有一些困难。他们必须考虑到第一个节点未用作列表的其他节点,并且第一个节点的数据成员 data 也未被使用。

通常列表的声明方式如下

struct List
{
    // some other stuff as for example constructors and member functions
    struct node
    {
        int data;
        struct node* next;    // this is fine
    } head;
};

List list = {};

或者在 C++ 中你可以简单地写

struct List
{
    // some other stuff as for example constructors and member functions
    struct node
    {
        int data;
        struct node* next;    // this is fine
    } head = nullptr;
};

List list;

当然你可以自己定义List的默认构造函数

在这种情况下,例如要检查列表是否为空,定义以下成员函数就足够了

struct List
{
    bool empty() const { return head == nullptr; }

    // some other stuff as for example constructors and member functions

    struct node
    {
        int data;
        struct node* next;    // this is fine
    } head;
};

这个问题没有确定的答案。您可以采用任何一种方式。这个问题的答案取决于你想如何组织你的链表以及你想如何表示一个列表。

您有两个选择:

  1. 没有 "dummy" 头元素的列表。在这种情况下,empty 列表由 head pointer

    中的 null 表示
    struct list* head = NULL;
    

    所以这就是您问题的答案:我们将其声明为指针,以便能够通过设置表示列表head 指向空的指针。

  2. 具有 "dummy" 头元素的列表。在这种情况下,列表的第一个元素不用于存储实际的用户数据:它只是作为列表的起始 "dummy" 元素。它被声明为

    struct list head = { 0 };
    

    以上表示一个空列表,因为 head.next 为 null 并且 head 对象本身 "does not count".

    即如果您愿意,您可以那样声明。请记住 head 并不是真正的列表元素。实际元素在 head.

  3. 之后开始

而且,一如既往,请记住,当您使用非动态分配的对象时,这些对象的生命周期受范围规则的约束。如果您想覆盖这些规则并手动控制对象的生命周期,那么您别无选择,只能动态分配它们,因此,使用指针。

简单来说,如果你的头是链表的起始节点,那么它只会包含链表开始的第一个节点的地址。这样做是为了避免混淆一般程序员。由于头部将仅包含地址,因此它被声明为指针。但是您想要声明的方式也可以,只需相应地编码即可。提示:如果您以后想在链表中进行一些更改,例如在链表开头进行删除或插入操作,您将面临问题,因为您将需要另一个指针变量。所以最好将第一个节点声明为指针。