Function Pointers and Callbacks: Sort, Search, Destroy

Post 7 of the Dynamic Arrays in C series · Full source code on GitHub Passing Behavior as Data Our array can store any type, grow on demand, handle errors without corrupting state, and communicate failures through typed error codes. What it cannot do is anything interesting with the elements inside it. We can push, get, set, and insert, operations that treat the elements as opaque byte blobs. We can’t sort the array because sorting requires knowing how to compare two elements. We can’t search it because searching requires knowing what “matching” means. We can’t even destroy it properly when the elements own heap-allocated resources, because the array doesn’t know how to clean them up. ...

June 18, 2026 · 16 min · pablo

Type-Safe Wrappers: Macros That Protect Your void*

Post 5 of the Dynamic Arrays in C series · Full source code on GitHub The Ergonomics Problem Post 4 left us with a working generic array. One struct, one push function, one get function, stores any fixed-size type. The void* engine works. But using it is miserable. To push an integer, you can’t just write array_push(arr, 42). You have to create a variable, then pass its address: int x = 42; array_push(arr, &x);. Two lines for something that should be one expression. To read a value back, you get a void* that you must cast manually: int *val = (int *)array_get(arr, 0);. Every access requires a cast. Every cast is an opportunity to lie to the compiler, write double *val = (double *)array_get(arr, 0) on an int array, and the compiler won’t say a word. You’ll get garbage at runtime and spend an hour wondering why. ...

June 8, 2026 · 14 min · pablo

Type Erasure: Generic Arrays with void* and memcpy

Post 4 of the Dynamic Arrays in C series · Full source code on GitHub The Limitation We’ve Been Ignoring For three posts, our array has stored integers. Only integers. If you wanted to store floats, you’d write a FloatArray, same struct, same logic, same push function, just with float instead of int everywhere. If you wanted to store a 20-byte struct, you’d write a StructArray. And another for double. And another for char*. Every new type means a new copy of the same code with the same bugs, the same growth logic, the same visualization, the same tests, just with a different type name. ...

June 3, 2026 · 15 min · pablo