libflint/docs/linkedlist.md

6.2 KiB

linkedlist

A dual sided linked list structure. Used as the foundation for many other modules in libflint

Usage

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);

Structs

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 list
  • destroy: Optional deallocation function for member data. Use NULL if data is stack allocated
  • match: 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 list
  • tail: Pointer to the ListNode at the tail of the list

ListNode

Node 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 node
  • next: Pointer to the ListNode before this node in the list
  • prev: Pointer to the ListNode after this node in the list

Functions

ll_init

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);

ll_destroy

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);

ll_ins_next

Creates a new node containing data and inserts it after node.

int ll_ins_next(List *list, ListNode *node, const void *data);

ll_ins_prev

Creates a new node containing data and inserts it before node.

int ll_ins_prev(List *list, ListNode *node, const void *data);

ll_remove

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);

ll_remove_next

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);

ll_remove_prev

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);

Macros

LL_ITER

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));
}