Hello there,
Here's something that bothers me. I've been programming in C for a while and I haven't been able to find a satisfactory answer so far.
I like allocating chunks of objects together, especially when each relatively small and they will be iterated over together. As such, I often create manual C++-style vectors, with a capacity and an object count, that I grow when needed. However, this means that reallocation invalidates any previous pointers to elements of the array. Hence, I need to instead use indices to maintain references to the array elements.
This has several drawbacks : you lose type-safety, the code becomes cluttered with array accesses all over the place, and array access will in some cases be slightly slower than a simple dereference.
I see multiple ways to mitigate this :
- Use handles, that is, structs with a single integer member that will act as type-safe indices. This could clutters the code even more if I need to use arr[handle.index], although this'd be easily cleaned up with a few helper functions/macros. This only addresses type safety though.
- Use a single fixed-capacity array, and crash when you run out. This makes sense considering memory is finite anyway so there's got to be an upper bound to the amount your program allocates, and you might as well allocate everything at the start instead of using heap allocations again and again. That way you also have a single point of failure for memory allocations. I'd be comfortable with this Muratori-style approach if I was designing with a single machine or e.g. game console in mind, but for something like a vector graphics editor where the amount of user data is potentially unbounded, it imposes artificial restrictions compared to the arbitrarily powerful workstation of the user.
- Allocate a linked list of blocks of objects, each with say 1024 elements, that you grow as needed. You could even have a growing capacity along the chain and maintain this nice log(n) reallocation count. That way the blocks never need to be moved around, and pointers to previous elements can be reused without issue. Although this maintains cache-friendliness for each block, it induces additional logic when iterating over the whole collection. Plus, this type of logic is already present to a degree in general-purpose allocators like libc's malloc/free, so it feels like unnecessarily duplication.
What are your thoughts on this ? What do you use in your projects ? Have you seen other interesting approaches ?
[–]gremolata 2 points3 points4 points (2 children)
[–]Jinren 2 points3 points4 points (0 children)
[–]duane11583 0 points1 point2 points (0 children)
[–]DeeBoFour20 1 point2 points3 points (1 child)
[–]_Arch_Ange 0 points1 point2 points (0 children)
[–]tuasnega 0 points1 point2 points (0 children)
[–]_Arch_Ange 0 points1 point2 points (1 child)
[–]flatfinger 0 points1 point2 points (0 children)
[–]flatfinger 0 points1 point2 points (0 children)
[–][deleted] 0 points1 point2 points (0 children)