This reference is organized by operation group and focuses on behavior contracts: purpose, preconditions, failure behavior, and complexity.
- Purpose: public size type for array counts, capacities, indexes, and slice lengths.
- Definition: alias of
size_t. - Header dependency: available through
array.h, which includes<stddef.h>. - Notes: use
array_size_tin APIs that interact with Arraylist metadata; cast tosize_twhen passing to standard-library formatting or APIs that explicitly requiresize_t.
- Purpose: owning dynamic array pointer for element type
T. - Generated by:
generate_array_type(T). - Shape: pointer to
ArrayStruct(T). - Public fields:
count: initialized element count.capacity: allocated element slots.elements: flexible array member containing element storage.
- Ownership: must be released with
array_free. - Invalidation: the pointer value may change after
array_reserveorarray_try_push, because growth can callrealloc.
- Purpose: concrete struct type behind
Array(T). - Generated by:
generate_array_type(T). - Shape:
typedef struct {
array_size_t count;
array_size_t capacity;
T elements[];
} ArrayStruct(T);- Notes: most call sites should use
Array(T)rather than namingArrayStruct(T)directly.
- Purpose: owner-relative half-open range over an
Array(T). - Generated by:
generate_array_type(T)ordecl_slice(T). - Public fields:
start: first array index in the range.count: number of elements in the range.
- Ownership: stores no pointer and owns no memory.
- Invalidation: does not dangle when the backing array is reallocated; it remains usable when the current array still has enough elements for
start + count.
- Purpose: temporary raw pointer view over contiguous elements of type
T. - Generated by:
generate_array_type(T)ordecl_span(T). - Public fields:
count: number of elements in the view.elements: borrowed pointer to the first element.
- Ownership: never pass a span to
array_free. - Invalidation: a span becomes dangling if the backing array/storage is freed or reallocated.
- Purpose: declare typed
Array(T),Slice(T), andSpan(T)forms. - Preconditions: run before first use of those generated types.
- Failure behavior: none (macro expansion).
- Complexity: compile-time only.
- Purpose: allocate a new
Array(T)with initialcapacity == sizeandcount == 0. - Preconditions: valid type
T;sizeconvertible toarray_size_t. - Failure behavior: returns
NULLon overflow or allocation failure. - Complexity:
O(1)allocation step (allocator-dependent). - Notes: use
size == 8as the recommended default for ordinary small/unknown lists. Use0when avoiding spare allocation matters, and use a larger estimate when one is known.
- Purpose: release array memory.
- Preconditions: pass the same owning pointer returned by
array_make/growth path. - Failure behavior: none;
free(NULL)is valid. - Complexity:
O(1)call (allocator-dependent release work).
- Purpose: ensure
arr->capacity >= min_capacity. - Preconditions:
arrmust be non-NULL.arrmust be a modifiable lvalue (may be updated afterrealloc).
- Failure behavior: returns
falseon null input, overflow, or allocation failure. - Complexity:
O(1)if existing capacity is enough.O(n)when reallocation moves/copies existing elements.
- Purpose: append one element at the logical end.
- Preconditions:
arrnon-NULL;arris a modifiable lvalue;valuecan be assigned to the array element type. - Failure behavior: returns
falseifarris null, count overflows, or growth fails. - Complexity: amortized
O(1), worst-caseO(n)on resize. - Notes: reserves capacity before assigning
valueintoarr->elements[arr->count], so normal C assignment diagnostics catch incompatible element types.
- Purpose: compatibility alias for
array_try_push. - Preconditions: same as
array_try_push. - Failure behavior: same as
array_try_push. - Complexity: same as
array_try_push.
- Purpose: append without surfacing failure in call sites.
- Preconditions: same as
array_try_push; caller accepts silent failure. - Failure behavior: internally ignores failed push.
- Complexity: same as
array_try_push.
- Purpose: checked element access by index; writes a pointer to the element inside the array (not a copy).
- Preconditions:
arrnon-NULLidx < arr->countout_ptrnon-NULL(address of aT*variable)
- Failure behavior: returns
falsewhen any precondition fails; does not write*out_ptron failure. - Complexity:
O(1). - Notes: the returned pointer is temporary and invalidated if the array is reallocated or freed. Use
array_try_getwhen a copy is enough.
- Purpose: direct index access.
- Preconditions:
arrnon-NULLandidxin range. - Failure behavior: undefined behavior if preconditions are violated.
- Complexity:
O(1).
- Purpose: checked element access by index; writes a copy of the element to
out_value. - Preconditions:
arrnon-NULLidx < arr->countout_valuenon-NULL(address of aTvariable)
- Failure behavior: returns
falsewhen any precondition fails; does not write*out_valueon failure. - Complexity:
O(1).
- Purpose: checked element assignment by index.
- Preconditions:
arrnon-NULLidx < arr->countvaluecan be assigned to the array element type.
- Failure behavior: returns
falsewhenarris null oridxis out of range. - Complexity:
O(1).
- Purpose: build checked owner-relative slice range for half-open range
[low, high). - Preconditions:
arrnon-NULLlow <= high <= arr->countout_slicenon-NULL
- Failure behavior: returns
falseon invalid bounds or null pointers. - Complexity:
O(1)(no element copy, no element pointer retained). - Notes: the resulting
Slice(T)storesstartandcount, so it is safe to keep across array growth as long as the current array still contains that range.
- Purpose: checked pointer access through a
Slice(T)range. - Preconditions:
arrnon-NULLidx < slice.countslice.start + slice.count <= arr->countout_ptrnon-NULL
- Failure behavior: returns
falsewhen any precondition fails. - Complexity:
O(1). - Notes: the returned pointer is temporary and invalidated if the array is reallocated or freed.
- Purpose: materialize a temporary raw pointer
Span(T)from a current array and aSlice(T)range. - Preconditions:
arrnon-NULLslice.start + slice.count <= arr->countout_spannon-NULL
- Failure behavior: returns
falsewhen the slice is no longer valid for the current array. - Complexity:
O(1). - Notes: the returned span is a borrowed pointer view and becomes dangling after backing storage is reallocated or freed.
- Purpose: build owner-relative slice range without validation.
- Preconditions:
arrnon-NULLand bounds valid. - Failure behavior: undefined behavior if preconditions are violated.
- Complexity:
O(1).
- Purpose: build an explicit temporary raw pointer span over arbitrary contiguous memory.
- Preconditions:
elementspoints to at leastcountelements, unlesscount == 0. - Failure behavior: undefined behavior if callers later use an invalid backing pointer.
- Complexity:
O(1).
- Purpose: safe pointer to last element.
- Preconditions: none.
- Failure behavior: returns
NULLwhenarrisNULLor empty. - Complexity:
O(1).
- Purpose: pointer to first element slot.
- Preconditions:
arrnon-NULL. - Failure behavior: undefined behavior if
arrisNULL. - Complexity:
O(1).
- Purpose: pointer to last element.
- Preconditions:
arrnon-NULLand non-empty. - Failure behavior: undefined behavior if preconditions are violated.
- Complexity:
O(1).
- Purpose: strict C typed pointer iteration from start to end.
- Preconditions:
arrnon-NULL. - Failure behavior: undefined behavior if
arrisNULL. - Complexity:
O(n)overcount.
- Purpose: inferred-type iteration where
typeofis available. - Preconditions:
ARRAY_HAS_TYPEOFis1(GNU/Clang, not strict ISO C) andarrnon-NULL. - Failure behavior: macro is unavailable when
ARRAY_HAS_TYPEOFis0; otherwise same preconditions as typed iteration. - Complexity:
O(n).
- Purpose: unchecked owner-relative slice range creation with inferred element type.
- Preconditions:
ARRAY_HAS_TYPEOFis1; bounds valid. - Failure behavior: unavailable in strict mode; undefined behavior if bounds are invalid.
- Complexity:
O(1).
- Purpose: unchecked raw span creation with inferred element type.
- Preconditions:
ARRAY_HAS_TYPEOFis1; backing pointer remains valid while the span is used. - Failure behavior: unavailable in strict mode; undefined behavior if callers later use an invalid backing pointer.
- Complexity:
O(1).
- Purpose: read logical size and emptiness.
- Preconditions:
arrnon-NULL. - Failure behavior: undefined behavior if
arrisNULL. - Complexity:
O(1).
- Purpose: nullable-safe metadata helpers.
- Preconditions: none.
- Failure behavior: none (
NULLmaps to0/true). - Complexity:
O(1).
- Default for new code: checked APIs (
array_try_*,array_reserve, nullable helpers). - Use unchecked compatibility APIs only when preconditions are guaranteed locally and reviewed.
The library is defensively written, but certain patterns consistently cause problems. These are not bugs in the implementation — they are sharp edges inherent to C macros and pointer semantics.
array_push(arr, value); // Silently does nothing on allocation failure
if (!array_try_push(arr, value)) // Preferred
goto fail;Any of these can invalidate previously obtained pointers:
array_reservearray_try_push/array_pusharray_free
Bad pattern:
int *p = NULL;
array_try_at(values, 0, &p);
array_try_push(values, 42); // May realloc and move the array
printf("%d\n", *p); // Undefined behaviorCorrect pattern:
int *p = NULL;
if (array_try_at(values, 0, &p)) {
printf("%d\n", *p);
}
if (!array_try_push(values, 42)) goto fail;
// Re-acquire the pointer after any growth
if (array_try_at(values, 0, &p)) {
printf("%d\n", *p);
}Slice(T) objects are safe across growth (they store indexes). Span(T) objects are not.
- A
Slicestoresstart+count. It remains valid as long as the range still exists in the current array. - A
Span(and any pointer fromarray_try_atorarray_back_ptr) stores a raw pointer and becomes dangling on realloc.
Always re-materialize Spans after growth:
Span(int) view;
if (array_try_span_t(int, arr, my_slice, &view)) { ... }array_end(arr) returns a pointer to the last element and is undefined behavior on empty arrays. The name is misleading to most C programmers (who expect "end" to mean one-past-the-end).
Use the safe, nullable version instead:
int *last = array_back_ptr(arr); // NULL when empty or NULL array
if (last) printf("%d\n", *last);array_end and array_end_unchecked are compatibility names for the unchecked path.
Passing a bare NULL literal to some checked macros can cause compile errors in strict -std=c11 -pedantic mode because of how the macros are written.
Recommended patterns (in order of preference):
// Best with the helper (new in this library)
array_try_get(array_null(int), 0, &v);
// Good
Array(int) missing = NULL;
array_try_get(missing, 0, &v);
// Also works
array_try_get( (Array(int))0 , 0, &v);These macros require a modifiable lvalue because growth may call realloc and update the pointer:
// BAD — the update happens to a local copy
Array(int) *p = &my_array;
array_reserve(*p, 100);
// GOOD
array_reserve(my_array, 100); // my_array itself may be reassignedPassing through a pointer is a common source of bugs:
void grow(Array(int) *p) {
array_reserve(*p, 100); // Updates the local *p, not caller's variable
}Never store Array(T) behind an extra level of indirection if you intend to grow it. The library is designed around direct lvalue use.
array_make(int, 5) followed by array_reserve(arr, 6) results in capacity 10, not 8. The policy doubles from the current capacity, not the next power of two. This is intentional but different from many other dynamic array libraries.
Define ARRAY_DEBUG (e.g. -DARRAY_DEBUG) before including the header to turn on extra runtime assertions. These catch invariant violations (such as count > capacity or invalid slices) early.
This is highly recommended while you are still learning the ownership and invalidation rules. The checks have zero cost when ARRAY_DEBUG is not defined.
- Zero-capacity arrays grow correctly on first push.
- Size arithmetic is overflow-checked before allocation.
- Failed growth leaves existing array data intact.
- Checked slice creation validates half-open bounds and output pointer.
Slice(T)ranges survive array reallocation;Span(T)and pointers do not.