Welcome to LibFP’s Documentorial!#
This documentation page is designed less like a direct reference and more like a tutorial. If you would instead prefer a reference… full generated documentation can be found here.
This is the C version of this document the C++ version can be found here.
This library provides “fat pointers” - pointers with embedded size information that enable runtime bounds checking and memory safety. Fat pointers store metadata (size, magic number) in a header just before the actual data, allowing automatic length tracking and validation. All while being fully compliant C pointers.
Features#
Single Header Library#
This library can be used standalone from the rest of the library… however certain
functions need FP_IMPLEMENTATION defined before the library is included to get definitions.
Be sure to #define FP_IMPLEMENTATION in only one source file!
Basic Usage Example#
// Allocate a fat pointer array, notice that the result is a standard pointer and completely
// compatible with all interfaces expecting a pointer.
int* arr = fp_malloc(int, 20);
// Access with automatic bounds information
for(size_t i = 0; i < fp_length(arr); i++)
arr[i] = i * 2;
// Reallocate, the one place where C standard library functions won't work is when
// reallocating or freeing fp memory... use the fp_ versions instead!
arr = fp_realloc(int, arr, 30);
// Clean up... see the note on fp_realloc
fp_free_and_null(arr);
-
fp_malloc(type, _size)
Allocate a typed fat pointer on the heap.
int* data = fp_malloc(int, 100); for(size_t i = 0; i < fp_length(data); i++) { data[i] = rand(); } fp_free_and_null(data);
- Parameters:
type – Element type
_size – Number of elements to allocate
- Returns:
Typed pointer to allocated heap memory
Warning
doxygenfunction: Cannot find function “fp_length” in doxygen xml output for project “fp_doxygen” from directory: /home/runner/work/libfp/libfp/build/xml
-
fp_realloc(type, p, _size)
Reallocate a fat pointer with a new size.
Existing data is preserved up to the minimum of old and new sizes.
float* values = fp_malloc(float, 50); // ... use values ... values = fp_realloc(float, values, 100); // Expand to 100 elements
- Parameters:
type – Element type
p – Existing fat pointer
_size – New number of elements
- Returns:
Reallocated typed pointer (may be different address)
-
fp_free_and_null(p)
Free a fat pointer and set it to NULL.
In general this macro is safer than using fp_free directly
int* data = fp_malloc(int, 50); fp_free_and_null(data); assert(data == NULL);
- Parameters:
p – Fat pointer to free
Stack Allocation Example#
void process_data() {
// Stack-allocated fat pointer (automatically freed)
float* temp = fp_alloca(float, 100);
// Use it like a normal array
for(size_t i = 0; i < fp_length(temp); i++)
temp[i] = i * 1.5f;
// Automatically freed when function returns
}
-
fp_alloca(type, _size)
Allocate a typed fat pointer on the stack.
Best practices:
Use fp_alloca() for temporary buffers < 10KB
Use fp_malloc() for anything larger or with dynamic lifetime
Never return stack-allocated pointers
Be careful with recursion and stack allocations
void compute() { int* numbers = fp_alloca(int, 50); for(int i = 0; i < 50; i++) { numbers[i] = i * i; } printf("Length: %zu\n", fp_length(numbers)); // Automatically freed }
See also
fp_alloca() for type-safe version
See also
fp_malloc() for heap allocation
See also
fp_is_stack_allocated() to check allocation type
Warning
Stack allocations have limited size. Use fp_malloc() for large allocations.
Warning
Stack memory is only valid within the current scope. Do not return stack-allocated pointers from functions.
- Parameters:
type – Element type
_size – Number of elements to allocate
- Returns:
Typed pointer to allocated stack memory
Views Example#
int* data = fp_malloc(int, 100);
// Create a view of a portion of the data
fp_view(int) subrange = fp_view_make(int, data, 10, 20);
// Iterate over the view
fp_view_iterate_named(int, subrange, item)
printf("%d\n", *item);
fp_free_and_null(data);
-
fp_view(type)
Define a view type for a specific element type (C version)
fp_view(int) my_view; fp_view(char) str_view;
- Parameters:
type – Element type
-
fp_view_make(type, p, start, length)
Create a view from a range within a fat pointer.
int* arr = fp_malloc(int, 100); // Create view of elements 10-29 (20 elements) fp_view(int) middle = fp_view_make(int, arr, 10, 20); fp_view_iterate(int, middle) { *i = 999; }
- Parameters:
type – Element type
p – Fat pointer
start – Starting index (inclusive)
length – Number of elements
- Returns:
Typed view of the specified range
-
fp_view_iterate_named(type, view, iter)
Iterate over view with named iterator.
fp_view(float) v = ...; fp_view_iterate_named(float, v, ptr) { *ptr *= 2.0f; }
- Parameters:
type – Element type
view – View structure
iter – Iterator variable name
Basic Dynamic Array Example#
// Create empty dynamic array
fp_dynarray(int) arr = NULL;
// Push elements (automatically grows)
fpda_push_back(arr, 10);
fpda_push_back(arr, 20);
fpda_push_back(arr, 30);
printf("Size: %zu, Capacity: %zu\n", fpda_size(arr), fpda_capacity(arr));
// Access elements
for(size_t i = 0; i < fpda_size(arr); i++)
printf("%d ", arr[i]);
fpda_free_and_null(arr);
-
fp_dynarray(type)
Define a dynamic array type.
Creates a typed dynamic array pointer. The array starts as NULL and grows automatically as elements are added.
fp_dynarray(int) integers = NULL; fp_dynarray(float) floats = NULL; fp_dynarray(MyStruct) structs = NULL;
- Parameters:
type – Element type
-
fpda_push_back(a, _value)
Add element to end of array.
Automatically grows array if needed. Amortized O(1) operation.
fp_dynarray(int) numbers = NULL; for(int i = 0; i < 100; i++) fpda_push_back(numbers, i * i); assert(fpda_size(numbers) == 100); assert(numbers[50] == 2500); fpda_free(numbers);
- Parameters:
a – Dynamic array
_value – Value to add
-
fpda_size
Alias for fpda_length.
Warning
doxygendefine: Cannot find define “fpda_capacity” in doxygen xml output for project “fp_doxygen” from directory: /home/runner/work/libfp/libfp/build/xml
-
fpda_free_and_null(da)
Free a dynamic array and set pointer to NULL.
Safer than fpda_free() as it prevents use-after-free.
fp_dynarray(int) arr = NULL; fpda_push_back(arr, 42); fpda_free_and_null(arr); assert(arr == NULL);
- Parameters:
da – Dynamic array to free
Common Dynamic Array Operations#
fp_dynarray(int) numbers = NULL;
// Reserve capacity
fpda_reserve(numbers, 100);
// Add elements
for(int i = 0; i < 50; i++)
fpda_push_back(numbers, i);
// Insert in middle
fpda_insert(numbers, 25, 999);
// Delete element
fpda_delete(numbers, 10);
// Pop from back
fpda_pop_back(numbers);
// Clear all elements (keeps capacity)
fpda_clear(numbers);
fpda_free_and_null(numbers);
-
fpda_reserve(a, _size)
Reserve capacity for at least _size elements.
Ensures the array can hold at least _size elements without reallocation. Does not change the size, only the capacity.
fp_dynarray(int) arr = NULL; // Reserve space for 1000 elements fpda_reserve(arr, 1000); // Add elements without reallocation for(int i = 0; i < 1000; i++) fpda_push_back(arr, i); // No reallocations fpda_free_and_null(arr);
- Parameters:
a – Dynamic array
_size – Minimum capacity to reserve
-
fpda_insert(a, _pos, _val)
Insert element at position.
Elements after _pos are shifted right. O(n) operation.
fp_dynarray(int) arr = NULL; fpda_push_back(arr, 1); fpda_push_back(arr, 2); fpda_push_back(arr, 4); fpda_push_back(arr, 5); // Insert 3 at index 2 fpda_insert(arr, 2, 3); // arr is now: [1, 2, 3, 4, 5] assert(fpda_size(arr) == 5); assert(arr[2] == 3); fpda_free_and_null(arr);
- Parameters:
a – Dynamic array
_pos – Position to insert at
_val – Value to insert
-
fpda_delete(a, _pos)
Delete single element.
fp_dynarray(int) arr = NULL; for(int i = 0; i < 5; i++) fpda_push_back(arr, i * 10); // arr: [0, 10, 20, 30, 40] fpda_delete(arr, 2); // arr: [0, 10, 30, 40] assert(fpda_size(arr) == 4); fpda_free_and_null(arr);
- Parameters:
a – Dynamic array
_pos – Position of element to delete
- Returns:
Modified a.
-
fpda_pop_back(a)
Remove last element.
fp_dynarray(int) stack = NULL; fpda_push_back(stack, 10); fpda_push_back(stack, 20); fpda_push_back(stack, 30); fpda_pop_back(stack); assert(fpda_size(stack) == 2); assert(stack[fpda_size(stack) - 1] == 20); fpda_free_and_null(stack);
- Parameters:
a – Dynamic array
- Returns:
Pointer to removed element (now invalid)
-
fpda_clear(a)
Clear all elements (keeps capacity)
Sets size to 0 but doesn’t free memory.
fp_dynarray(int) arr = NULL; fpda_reserve(arr, 100); for(int i = 0; i < 50; i++) fpda_push_back(arr, i); assert(fpda_size(arr) == 50); assert(fpda_capacity(arr) == 100); fpda_clear(arr); assert(fpda_size(arr) == 0); assert(fpda_capacity(arr) == 100); // Capacity unchanged fpda_free_and_null(arr);
- Parameters:
a – Dynamic array
Advanced Dynamic Array Features#
fp_dynarray(float) data = NULL;
// Grow and initialize
fpda_grow_and_initialize(data, 10, 3.14f);
// Swap elements
fpda_swap(data, 0, 9);
// Clone array
fp_dynarray(float) copy = fpda_clone(data);
// Concatenate arrays
fpda_concatenate_in_place(data, copy);
// Shrink to fit (free excess capacity)
fpda_shrink_to_fit(data);
fpda_free_and_null(data);
fpda_free_and_null(copy);
-
fpda_grow_and_initialize(a, _to_add, _value)
Grow array and initialize new elements.
fp_dynarray(int) arr = NULL; fpda_push_back(arr, 1); fpda_push_back(arr, 2); // Add 5 more elements, all initialized to 99 fpda_grow_and_initialize(arr, 5, 99); assert(fpda_size(arr) == 7); assert(arr[2] == 99); assert(arr[6] == 99); fpda_free_and_null(arr);
- Parameters:
a – Dynamic array
_to_add – Number of elements to add
_value – Value to initialize new elements with
-
fpda_swap(a, _pos1, _pos2)
Swap two elements.
fp_dynarray(int) arr = NULL; fpda_push_back(arr, 10); fpda_push_back(arr, 20); fpda_push_back(arr, 30); fpda_swap(arr, 0, 2); // arr: [30, 20, 10] assert(arr[0] == 30); assert(arr[2] == 10); fpda_free_and_null(arr);
- Parameters:
a – Dynamic array
_pos1 – First position
_pos2 – Second position
-
fpda_clone(src)
Create a clone of dynamic array.
fp_dynarray(int) original = NULL; for(int i = 0; i < 5; i++) fpda_push_back(original, i * i); fp_dynarray(int) clone = fpda_clone(original); clone[0] = 999; // Doesn't affect original assert(original[0] == 0); assert(clone[0] == 999); fpda_free_and_null(original); fpda_free_and_null(clone);
- Parameters:
src – Source array
- Returns:
New array with copied data
-
fpda_concatenate_in_place(dest, src)
Concatenate two arrays in place.
fp_dynarray(int) arr1 = NULL; fp_dynarray(int) arr2 = NULL; fpda_push_back(arr1, 1); fpda_push_back(arr1, 2); fpda_push_back(arr2, 3); fpda_push_back(arr2, 4); fpda_concatenate_in_place(arr1, arr2); // arr1: [1, 2, 3, 4] // arr2: [3, 4] (unchanged) fpda_free_and_null(arr1); fpda_free_and_null(arr2);
- Parameters:
dest – Destination array (modified)
src – Source array
-
fpda_shrink_to_fit(a)
Shrink capacity to match current size.
Frees unused capacity. Useful after removing many elements.
fp_dynarray(int) arr = NULL; fpda_reserve(arr, 1000); for(int i = 0; i < 100; i++) fpda_push_back(arr, i); assert(fpda_capacity(arr) == 1000); assert(fpda_size(arr) == 100); fpda_shrink_to_fit(arr); assert(fpda_capacity(arr) == 100); // Freed 900 elements worth of memory fpda_free_and_null(arr);
- Parameters:
a – Dynamic array