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



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

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

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

时,混乱就开始了
struct list* head;

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

吗?
struct list head;

的地址用于进一步使用?

这个问题没有明确的答案。两种方法都可以。这个问题的答案取决于您希望如何组织链表以及如何表示列表。

你有两个选择:

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

    中由null表示。
    struct list* head = NULL;
    

    所以这是你的问题的答案:我们声明它作为一个指针能够表示一个列表设置head指针为null。

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

    struct list head = { 0 };
    

    上面表示一个空列表,因为head.next是空的,head对象本身"不算数"。

    。如果你愿意,你可以这样声明。请记住,head实际上不是一个列表元素。实际元素从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;
};

简单来说,如果您的head是链表的开始节点,那么它将只包含链表开始的第一个节点的地址。这样做是为了避免普通程序员的困惑。由于头只包含地址,因此它被声明为指针。但是您想要声明的方式也很好,只需相应地编写代码。提示:如果您稍后想要对链表进行一些更改,例如在链表的开头进行删除或插入操作,您将面临需要另一个指针变量的问题。因此最好将第一个节点声明为指针。

最新更新