Please read the question carefully, Considering this implementation, why would anyone use the list's Tail or End to mark the last element in a linked list instead of just using 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;
}
}
}
in main() the call would look like
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);
}
would there be advantages or disadvantages of using this method instead of using NULL
You have clarified in the comments that you are asking about ->Next = End
.
What are you doing is using the address of the tail pointer to signify the end of the list. This is a horrible idea.
There's absolutely no reason to do so. People know what encountering NULL means. Using confusing constants just hinders readability with no benefit.
It's error prone. End
now has two unrelated purposes. Confusing code like this is exactly what software developers try to avoid. The most important job of a programmer is to produce readable code.
And it's a lot less safe. While trying to follow a NULL
by accident will terminate your program in a very consistent and easy to debug manner, following an otherwise-valid pointer doesn't have those benefits.
You're not even consistent, using a combination of NULL
, Start
and End
to indicate a lack of node. Just use NULL
instead of inventing two new worthless and harmful constants.
A cleaner implementation:
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;
}