File: src/list.c
Header: include/list.h
Author: gvrio
A generic, singly-linked list implementation in C. Supports storing elements of any type using void* pointers and user-provided print and comparison functions.
This library was written as part of a learning journey in C and data structures.
.
├── demo/
│ └── demo.c # demo of usage
├── include/
│ └── list.h # header for imports
├── src/
│ └── list.c # source code
This linked list implementation is generic, type-agnostic, and designed for beginners to explore pointers, memory, and data structures. Each node stores a copy of the data for primitives types, or a pointer for structs.
Users must provide:
- A
printfunction for displaying elements. - A
cmpfunction for comparing elements.
- A C compiler (GCC, Clang, etc.)
- Optional: Make to simplify building
# Clone the repo
git clone https://github.com/gvrio/generic-linked-list.git
# Navigate to the demo folder
cd generic-linked-list
# Compile the demo with library
gcc demo/demo.c src/list.c -I include/ -o demo_exec
# Run the demo executable
./demo_exectypedef struct Node {
void *data;
struct Node *next;
} node_t;data: Pointer to stored element (copied).next: Pointer to next node.
typedef struct LinkedList {
node_t *head;
size_t dsize;
void (*print)(void*);
int (*cmp)(void*, void*);
} list_t;head: Pointer to first node.dsize: Size of each element (in bytes).print: Print function pointer for elements.cmp: Comparison function pointer for elements.
| Data Kind | Ownership | Allocation | Freed By | Description |
|---|---|---|---|---|
| Primitives (int, float, etc.) | Node (library) | Inside init_node() |
Library (delete*()) |
Node owns a copy of the primitive; safe to free() directly. |
| Strings | Node (library) | Deep-copied via malloc |
Library (delete*()) |
Node makes a separate heap copy, so deleting node frees the string safely. |
| Structs | User | User-managed | User | User owns the memory; user responsible for freeing if structs have nested pointers. |
| Lists (nested) | User | User-managed | User (via destroy()) |
User must call library-provided destroy() recursively to free each nested list safely. |
list_t *init_list(size_t size, void (*printfn)(void*), int (*cmpfn)(void*, void*));- Creates an empty list.
- Returns a pointer to
list_torNULLon failure.
Parameters:
size—sizeof(element_type)printfn— Function to print an elementcmpfn— Function to compare two elements
All insertion functions return int:
0→ success1→ failure
| Function | Description |
|---|---|
allocateh(list_t* list, void* data) |
Insert at head |
allocatet(list_t* list, void* data) |
Insert at tail |
allocaten(list_t* list, void* data, void* key) |
Insert after first node->data matching key |
All deletion functions return int:
0→ success1→ failure
| Function | Description |
|---|---|
deleteh(list_t* list) |
Delete head node |
deletet(list_t* list) |
Delete tail node |
deleten(list_t* list, void* key) |
Delete first node->data matching key |
void traverse(list_t* list);- Prints each element using the list's
printfunction. - Output format:
elem1 -> elem2 -> ... -> NULL. - Does not print a newline to remain composable for nested lists (so
printList()can embed traverse output).
void printInt(void* num);
void printChar(void* c);
void printStr(void* string);
void printFloat(void* num);
void printList(void* list);- Provided helpers for common types.
printListrecursively prints nested lists (it usestraverse()internally).
void destroy(list_t* list);- Frees all nodes and their stored data.
- Safe to call on an empty or already-empty list pointer (function checks for
NULLinternally).
int cmpInt(void* a, void* b);
int cmpChar(void* a, void* b);
int cmpStr(void* a, void* b);
int cmpFloat(void* a, void* b);
int cmpList(void* a, void* b);- Return
0if elements match, non-zero otherwise. - These functions are convenient to pass into
init_list()when storing these common types.
| Value | Meaning |
|---|---|
0 |
Success |
1 |
Failure (NULL pointer, allocation error, key not found) |
#include "list.h"
#include <stdio.h>
int main() {
list_t *list = init_list(sizeof(int), printInt, cmpInt);
int a = 10, b = 20;
allocateh(list, &a);
allocatet(list, &b);
printf("My list: ");
traverse(list); // Output: 10 -> 20 -> NULL
printf("\n");
destroy(list);
return 0;
}| Operation | Time Complexity |
|---|---|
| Insert head | O(1) |
| Insert tail | O(n) |
| Insert after key | O(n) |
| Delete head | O(1) |
| Delete tail | O(n) |
| Delete by key | O(n) |
| Traverse | O(n) |