I'm learning C for mainly for fun and to get a better understanding of how things work under the hood (most of my experience lies with high-level languages). I've been following quite a few books and working through the exercises. For extra practice, I've implemented a linked list using the knowledge I've learned - I thought it might be useful to get some feedback on it!
I plan to make this generic and work with any type once I learn how to do this :)
I desire any and all feedback. Especially feedback that pushes me in the right direction (except for 'give up'!)
llist.h:
#ifndef __llist_
#define __llist_
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#define LLMSG_ELISTNULL "List cannot be NULL"
#define LLMSG_EHEADNULL "Head cannot be NULL - list corrupted"
#define LLMSG_ENODENULL "Node cannot be NULL"
#define LLMSG_ENEWNULL "New node cannot be NULL"
typedef struct _llist_node {
struct _llist_node *next;
int value;
} llist_node;
typedef struct _llist {
struct _llist_node *head;
} llist;
/*
constructors & destructor
*/
llist_node *llist_node_create(int value);
llist *llist_create(llist_node *head);
void llist_destroy(llist *list);
/*
insertion
*/
llist *llist_append(llist *list, llist_node *node);
llist *llist_prepend(llist *list, llist_node *node);
llist_node *llist_insert_after(llist_node *node, llist_node *new);
llist_node *llist_insert_before(llist *list, llist_node *node, llist_node *new);
/*
removal
*/
llist *llist_remove_head(llist *list);
llist_node *llist_remove_after(llist_node *node);
llist *llist_remove(llist *list, llist_node *node);
#endif
llist.c:
#include "llist.h"
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
llist_node *llist_node_create(int value) {
llist_node *node = malloc(sizeof(llist_node));
if (! node) {
errno = ENOMEM;
return NULL;
}
node->value = value;
node->next = NULL;
return node;
}
llist *llist_create(llist_node *head) {
assert(head != NULL && LLMSG_ENODENULL);
llist *list = malloc(sizeof(llist));
if (list == NULL) {
errno = ENOMEM;
return NULL;
}
list->head = head;
return list;
}
void llist_destroy(llist *list) {
assert(list != NULL && LLMSG_ELISTNULL);
// do not proceed if list container corrupt
if (list->head != NULL) {
llist_node *head = list->head;
llist_node *next;
llist_node *next_next;
// free rest of list first
if (head->next != NULL) {
next = head->next;
while (next != NULL) {
next_next = next->next;
free(next);
next = next_next;
}
}
if (head != NULL) {
free(head);
}
}
free(list);
}
llist *llist_append(llist *list, llist_node *node) {
assert(list != NULL && LLMSG_ELISTNULL);
assert(node != NULL && LLMSG_EHEADNULL);
assert(list->head != NULL && LLMSG_EHEADNULL);
llist_node *next;
if (list->head->next == NULL) {
list->head->next = node;
}
else {
for (next = list->head->next; next != NULL; next = next->next) {
if (next->next == NULL) {
next->next = node;
break;
}
}
assert(next->next == node && "Failed to insert node to end of list");
}
return list;
}
llist *llist_prepend(llist *list, llist_node *node) {
assert(list != NULL && LLMSG_ELISTNULL);
assert(node != NULL && LLMSG_ENODENULL);
assert(list->head != NULL && LLMSG_EHEADNULL);
llist_node *head = list->head;
list->head = node;
node->next = head;
return list;
}
llist_node *llist_insert_after(llist_node *node, llist_node *new) {
assert(node != NULL && LLMSG_ENODENULL);
assert(new != NULL && LLMSG_ENEWNULL);
llist_node *next = node->next;
node->next = new;
new->next = next;
return new;
}
llist_node *llist_insert_before(llist *list, llist_node *node, llist_node *new) {
assert(list != NULL && LLMSG_ELISTNULL);
assert(node != NULL && LLMSG_ENODENULL);
assert(new != NULL && LLMSG_ENEWNULL);
llist_node *curr = list->head;
while (curr->next != NULL) {
if (node == curr->next) {
curr->next = new;
new->next = node;
break;
}
curr = curr->next;
}
return new;
}
llist *llist_remove_head(llist *list) {
assert(list != NULL && LLMSG_ELISTNULL);
llist_node *head = list->head;
if (list->head->next) {
list->head = list->head->next;
}
free(head);
return list;
}
llist_node *llist_remove_after(llist_node *node) {
assert(node != NULL && LLMSG_ENODENULL);
if (node->next != NULL) {
llist_node *delete = node->next;
node->next = node->next->next;
free(delete);
}
return node;
}
llist *llist_remove(llist *list, llist_node *node) {
assert(list != NULL && LLMSG_ELISTNULL);
assert(node != NULL && LLMSG_ENODENULL);
llist_node *curr = list->head;
while (curr->next != NULL) {
if (node == curr->next) {
curr = llist_remove_after(curr);
break;
}
curr = curr->next;
}
return list;
}
linked-list.c (test output for each operation):
#include <stdio.h>
#include "llist.h"
int main() {
llist *list = llist_create(llist_node_create(1));
list = llist_append(list, llist_node_create(2));
list = llist_append(list, llist_node_create(3));
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
list = llist_prepend(list, llist_node_create(-1));
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
printf("4: %d\n", list->head->next->next->next->value);
llist_insert_after(list->head->next, llist_node_create(400));
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
printf("4: %d\n", list->head->next->next->next->value);
printf("5: %d\n", list->head->next->next->next->next->value);
llist_insert_before(list, list->head->next->next, llist_node_create(500));
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
printf("4: %d\n", list->head->next->next->next->value);
printf("5: %d\n", list->head->next->next->next->next->value);
printf("6: %d\n", list->head->next->next->next->next->next->value);
list = llist_remove_head(list);
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
printf("4: %d\n", list->head->next->next->next->value);
printf("5: %d\n", list->head->next->next->next->next->value);
list = llist_remove(list, list->head->next->next);
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
printf("4: %d\n", list->head->next->next->next->value);
list->head = llist_remove_after(list->head);
printf("1: %d\n", list->head->value);
printf("2: %d\n", list->head->next->value);
printf("3: %d\n", list->head->next->next->value);
llist_destroy(list);
return 0;
}