Something went wrong. Try again.
A header-behind-pointer dynamic array implementation
c dynamic array
Something went wrong. Try again.
C 77%
Just 15%
Nix 5%
Shell 3%
README.md
dynarr #
A type-safe dynamic array library for C11, using a header-behind-pointer layout so the array pointer can be indexed and passed to functions just like a plain C array.
Setup #
The project uses Nix flakes + direnv:
direnv allow . # once, to activate the shell
This provides gcc, just, bear, cppcheck, clang-format, and clangd (plus valgrind on Linux).
Building #
just build # static (.a) and dynamic (.so/.dylib) into .build/
just static # static only
just dynamic # dynamic only
Pre-built artifacts are in lib/static/ and lib/dynamic/. To link against them from another project:
gcc -Ipath/to/dynarr/include main.c -Lpath/to/dynarr/lib/static -lmylib
Testing #
just test # all tests, with ASan + UBSan
just valgrind # all tests under Valgrind (Linux only)
To run a single test binary directly:
.build/tests/test_dynarr
API reference #
All operations are macros. arr must be a typed pointer variable (e.g. int *arr); it may be reassigned by operations that realloc.
Lifecycle #
| Macro | Description |
|---|---|
da_new(type) |
Allocate an empty array. Assign to a type * variable. |
da_free(arr) |
Free the array. |
da_copy(arr) |
Return a new, independent copy. The copy is tight: capacity == count. |
Adding elements #
| Macro | Description |
|---|---|
da_append(arr, elem) |
Append one element, growing if needed. |
da_extend(arr, ptr, n) |
Append n elements from a plain C array ptr. |
da_reserve(arr, n) |
Ensure capacity for at least n elements. No-op if already sufficient. |
Removing elements #
| Macro | Description |
|---|---|
da_pop(arr) |
Remove and return the last element. |
da_swap_remove(arr, i) |
O(1) removal: overwrites index i with the last element, then decrements count. Does not preserve order. Bounds-checked with assert. |
da_clear(arr) |
Reset count to 0, keeping the allocation. |
Accessing elements #
| Macro | Description |
|---|---|
arr[i] |
Direct unchecked access (standard C indexing). |
da_get(arr, i) |
Bounds-checked access via assert. |
da_last(arr) |
The last element (no bounds check). |
Inspecting state #
| Macro | Description |
|---|---|
da_length(arr) |
Number of elements (size_t). |
da_capacity(arr) |
Current allocated capacity (size_t). |
da_empty(arr) |
1 if count == 0, else 0. |
Usage example #
#include "dynarr.h"
typedef struct { float x, y; } Vec2;
int main(void) {
// Basic append and iterate
int *nums = da_new(int);
for (int i = 0; i < 10; i++)
da_append(nums, i);
for (size_t i = 0; i < da_length(nums); i++)
printf("%d\n", nums[i]);
// Bulk load from a plain C array
int extra[] = {10, 11, 12};
da_extend(nums, extra, 3);
// O(1) removal (order not preserved)
da_swap_remove(nums, 0);
// Copy, then free both independently
int *copy = da_copy(nums);
da_free(nums);
da_free(copy);
// Works with any type
Vec2 *pts = da_new(Vec2);
da_reserve(pts, 64);
Vec2 p = {1.0f, 2.0f};
da_append(pts, p);
da_free(pts);
return 0;
}
Other commands #
just fmt # auto-format all .c/.h files
just fmt-check # verify formatting without modifying
just cppcheck # static analysis
just compile-commands # generate compile_commands.json for clangd
just clean # remove .build/