Source linked

C23 Dynamic Array Macro traspasa la capacidad y la estructura completamente

gist.github.com@systems_wire4 hours ago·Systems Engineering·1 comments

Un encabezado C que utiliza dos punteros y potencia de dos creces elimina el almacenamiento de capacidad y el nombramiento de estructuras, basándose en las expresiones uintptr_t y la declaración.

c23gnu cuintptr tdynamic arraysystems programmingalurm

A 2-line dynamic array declaration — int *vec = {0}; — gives you a fully functional, resizable array with no struct definition and no capacity field. Alurm’s gist shows how to pull this off in C23 using nothing but statement expressions and a clever pointer-length encoding.

The Two-Pointer Trick Every dynamic array is just an array of two pointers. vec holds the length cast to uintptr_t; vec points to the actual data. No IntVec, no VecHeader — the name of the variable is the type. vec_push is a macro that takes the vec and a value, reallocates when needed, and returns true on success. Syntactic sugar aside, this works because uintptr_t is guaranteed to be wide enough to hold a pointer and an integer. The code casts vec to typeof(vec ) after incrementing the length, which keeps the compiler happy and the data contiguous.

Capacity on the Fly Capacity isn’t stored at all. vec_push checks whether the current length is zero or a power of two using (len & (len - 1)) == 0. If so, it calls realloc with the next power of two. This means reallocation happens only at lengths 0, 1, 2, 4, 8, 16 … giving the usual exponential growth without storing an extra word. But there’s a trade-off: you can’t reserve a large block ahead of time. If you manually realloc to a big capacity, the macro will still trigger a realloc every time the length hits a power of two, effectively discarding your pre-allocation. The gist’s author calls this out explicitly — the design is optimized for push-heavy use cases, not reservation-heavy ones.

The Catch: Implementation-Defined and GNU C Storing a length as uintptr_t in a pointer location is implementation-defined behavior. It assumes the pointer’s bit pattern can hold any integer value you put there and that reading it back via cast yields the same value. On most platforms (x86-64, ARM64) this works fine, but it’s not portable to, say, CHERI or other capability-based architectures. Second, the macro relies on GNU C statement expressions, a feature standardised in C23 but still a GCC/Clang extension in practice. MSVC won’t compile this without a compatibility shim. For embedded systems or code-golf-style C projects where every byte counts, this trick eliminates an entire struct definition and a capacity field. Expect to see this pattern show up in constrained environments where the simplicity of int *vec beats the ceremony of a proper vector library.


Source: A generic dynamic array in C that stores no capacity and needs no struct
Domain: gist.github.com

Read original source ->

External source stays available while the OJO article and comment thread stay local.

Comments load interactively on the live page.