A dual sided linked list structure. Used as the foundation for many other modules in libflint
Create the list. The user is responsible for memory management of the List
struct.
List *list = malloc(sizeof(List));
After the tree is created, init it. The second argument on bintree_init()
is an optional memory freeing function pointer with signature void (*destroy)(void *data)
. Use free()
from the stdlib if you are creating the data with malloc()
. If allocation of your data is more complex, you can pass your own memory deallocation function as long as it fits the signature.
In this example, we are passing NULL
because all memory will be stack allocated.
ll_init(list, NULL); int i = 1; int j = 2; int k = 4;
Next, insert data into the list. Data can be inserted from either side of a node.
ll_ins_next(list, list->head, (void *) &i); ll_ins_next(list, list->tail, (void *) &j); ll_ins_next(list, list->tail, (void *) &k); /* List is now 1 2 4 */
Lists can have a node removed by either targeting the node directly or removing a node pointed to by the head or tail of another node. The user is responsible for the memory management of the data inside the node, which is why a void*
must be supplied to hold the data. Since in this example our memory is stack allocated, we don’t need to worry about freeing the void*
but one must still be supplied for the function to work.
/* List is 1 2 4 */ /* node is the 2nd node in the list with value 2 */ ListNode *node = list->head; /* void pointer to the data inside of the node */ void *data; ll_remove(list, node, &data); /* List is now 1 4 */ printf("Removed: %d\n", *((int *) data)); /* Prints "2" */
Here is an alternative example using ll_remove_next()
instead of ll_remove()
/* node is the 2nd node in the list with value 2 */ ListNode *node = list->head; void *data; /* void pointer to the data inside of the node */ ll_remove_next(list, node, &data); /* List is now 1 2 */ printf("Removed: %d\n", *((int *) data)); /* Prints "4" */
To destroy the list, first call ll_destroy()
to free the nodes in the list and optionally run the destroy function against the data stored in the list. Since this example is stack allocated and we passed NULL
when creating the list, no destroy function is run against the memory in the nodes. The list itself must be deleted with free()
ll_destroy(list); free(list);
Complete example:
List *list = malloc(sizeof(List)); ll_init(list, NULL); int i = 1; int j = 2; int k = 4; ll_ins_next(list, list->head, (void *) &i); ll_ins_next(list, list->tail, (void *) &j); ll_ins_next(list, list->tail, (void *) &k); printf("List: "); print_ll(list); void *data; ll_remove_next(list, list->head, &data); printf("List: "); print_ll(list); printf("Removed: %d\n", *((int *) data)); ll_destroy(list); free(list);
Double ended linked list struct
typedef struct { size_t size; void (*destroy)(void *data); int (*match)(const void *a, const void *b); struct ListNode *head; struct ListNode *tail; } List;
Members:
size
: How many nodes are in the listdestroy
: Optional deallocation function for member data. Use NULL
if data is stack allocatedmatch
: Comparison function between data inside two nodes. This is not used by List
itself, but is used in structures built on top of List
head
: Pointer to the ListNode
at the head of the listtail
: Pointer to the ListNode
at the tail of the listNode of the list
typedef struct ListNode { void *data; struct ListNode *next; struct ListNode *prev; } ListNode;
Members:
data
: void pointer to the member data in the nodenext
: Pointer to the ListNode
before this node in the listprev
: Pointer to the ListNode
after this node in the list
Initialize the list. User is responsible for creating the initial List
structure and freeing memory with ll_destroy()
. destroy
is a deallocation function for the members of List
, pass NULL
if the memory is stack-allocated
void ll_init(List *list, void (*destroy)(void *data)); /* Usage */ List *list = malloc(sizeof(Stack)); // Pass NULL for stack-allocated memory ll_init(list, NULL); // Pass free or a custom freeing function for heap allocated memory ll_init(list, free);
Destroys the nodes inside a list and calls the deallocation funciton on the data if one was provided. Does not destroy the list itself, that is left up to the user.
void ll_destroy(List *list);
Creates a new node containing data
and inserts it after node
.
int ll_ins_next(List *list, ListNode *node, const void *data);
Creates a new node containing data
and inserts it before node
.
int ll_ins_prev(List *list, ListNode *node, const void *data);
Removes and deallocates the node pointed to by node
. The user is responsible for managing the contained data’s memory, as such the destroy
function is not run by ll_remove()
int ll_remove(List *list, ListNode *node, void **data);
Removes and deallocates the node pointed to by node
’s tail
pointer. The user is responsible for managing the contained data’s memory, as such the destroy
function is not run by ll_remove_next()
int ll_remove_next(List *list, ListNode *node, void **data);
Removes and deallocates the node pointed to by node
’s head
pointer. The user is responsible for managing the contained data’s memory, as such the destroy
function is not run by ll_remove_prev()
int ll_remove_prev(List *list, ListNode *node, void **data);
A utility macro that helps with iterating over an entire list. A ListNode
pointer named node
is created as part of this macro and can be used to access the current node in the iteration, so be careful your own variable naming if you intend to use LL_ITER
.
#define LL_ITER(list) for(ListNode *node = (list)->head; node != NULL; node = node->next) /* Example with list of strings */ for LL_ITER(list) { printf("%s\n", (char *)(node->data)); }