为什么要使最后一个 node.next 指向 Tail 而不是链表中的 NULL?

Why to make the last node.next point to Tail instead of NULL in a linked-list?

请仔细阅读问题,考虑到这个实现,为什么有人会使用 list 的 Tail 或 End 来标记链表中的最后一个元素 而不是只使用 NULL

#include <stdlib.h>
#include <stdio.h>

struct Node {
    int ID;
    struct Node* Previous;
    struct Node* Next;
};

void AddNodeAtEnd(struct Node** Start, struct Node** End, struct Node* NewNode) {
    if (*Start == *End && *End == NULL) {
        *Start = NewNode;
        *End = NewNode;
        NewNode->Previous = Start; // normal implementation would do this NewNode->Previous = NULL;
        NewNode->Next = End; // // normal implementation would do this NewNode->Next = NULL; 
    }
    else {
        struct Node* OldNode = *End;
        OldNode->Next = NewNode;
        NewNode->Previous = OldNode;
        NewNode->Next = End;
        *End = NewNode;
    }
}

void PrintList_Method1(struct Node** Start, struct Node** End) {
    if (*Start == NULL && *End == NULL) {
        printf("\nThe List Is Empty!\n");
        return;
    }
    else
    {
        struct Node* CurrentNode = *Start;
        while (CurrentNode != End)
        {
            printf("ID = %d\n", CurrentNode->ID);
            CurrentNode = CurrentNode->Next;
        }
    }
}

在 main() 中调用看起来像

int main()
{
    struct Node* List1Start = NULL;
    struct Node* List1End = NULL;

    struct Node* N1 = malloc(sizeof(struct Node));
    N1->ID = 15;
    struct Node N2 = malloc(sizeof(struct Node));;
    N2->ID = 30;
    PrintList(&List1Start, &List1End);
    AddNodeAtEnd(&List1Start, &List1End, N1);
    AddNodeAtEnd(&List1Start, &List1End, N2);
    PrintList(&List1Start, &List1End);
}

使用此方法而不是使用 NULL 会有优点或缺点吗

您已在评论中澄清了您所询问的 ->Next = End

你在做什么是用尾指针的地址来表示列表的结尾。这是一个可怕的想法。

绝对没有理由这样做。人们知道遇到 NULL 意味着什么。使用令人困惑的常量只会阻碍可读性,没有任何好处。

它很容易出错。 End 现在有两个不相关的目的。像这样令人困惑的代码正是软件开发人员试图避免的。程序员最重要的工作是编写可读代码。

而且安全性要差很多。虽然不小心尝试遵循 NULL 会以非常一致且易于调试的方式终止您的程序,但遵循其他方面有效的指针并没有这些好处。

你甚至不一致,使用 NULLStartEnd 的组合来表示缺少节点。只需使用 NULL 而不是发明两个新的无价值且有害的常量。


更简洁的实现:

typedef struct Node {
    int value;
    struct Node* prev;
    struct Node* next;
} Node;

typedef struct {
   Note *head;
   Node *tail;
} List;

void List_append(List *list, Node *node) {
   Node **tail_ptrptr = list->tail == NULL ? &( list->tail ) : &( tail->next );
   node->prev = *tail_ptrptr;
   node->next = NULL;
   *tail_ptrptr = node;
   list->tail = node;
}

void List_print(List *list) {
   for (Node node = list->head; node; node = node->next) {
      printf("%d\n", node->value);
   }
}

int main(void) {
   List list = { 0 };

   List_append(&list, create_node(15));
   List_append(&list, create_node(30));

   List_print(&list);

   // ...

   return 0;
}