Связанные списки могут иметь одинарные или двойные указатели. Одиночные связанные списки имеют указатель на следующий элемент, двойные связанные списки имеют указатели на следующий элемент и предыдущий элемент. Вы выбрали один связанный список.
Ваша функция для добавления в конец списка должна проходить через весь список при каждой операции добавления в список. Это означает, что добавление в список является операцией сложности O (n). Основное преимущество связанного списка заключается в операциях добавления и удаления O (1), если они реализованы для предоставления этой возможности.
Объявление списка как указателей на заголовок (списка) и хвост (списка),
typedef struct {
Node* head;
Node* tail;
} List;
Вместо того, чтобы объявлять список как указатель на указатель на узел,
typedef struct Node** List;
Теперь сложность добавления в список и удаления из него составляет O (1) (операции с постоянным временем ...
//enqueue to tail of list (not push)
void enqueue(List* list, char *input) {
if( !list ) return; //no list
Node* new = new_node(input); //new node
if( NULL == list->head ) { //list empty
list->head = new; //add at tail, tail equals head
}
else {
list->tail->next = new; //list not empty, add at tail
}
list->tail = new;
}
//dequeue from head of list
Node* dequeue(List* list) {
if( !list ) return NULL; //no list
if( NULL == list->head ) return NULL; //list empty
Node* node = list->head; //node at head of list
list->head = list->head->next; //remove from list
if( NULL == list->head ) list->tail = NULL; //empty now
//oh, should also: node->next = NULL;
return node;
}
И, как уже говорили другие, вы должны инициализировать все указатели в вашем "конструкторе",
Node *new_node(char *input) {
Node *new;
if( ! (new = malloc(sizeof(Node))) ) return NULL;
//new = calloc(sizeof(Node)); //calloc fills allocated memory with zero bytes
//initialize all values in Node structure
new->value = input;
new->next = NULL; //either use calloc or init the individual elements
return new;
}