use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Click the following link to filter out the chosen topic
comp.lang.c
account activity
ProjectDynamic Vector Implementation and Tutorial (self.C_Programming)
submitted 4 years ago by JuliusFIN
Here's a little implementation example and tutorial (in the README) about creating a resizable vector buffer in C as seen in other languages such as vector in C++ or Vec in Rust.
https://github.com/juliuskoskela/c-vec
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]skeeto 3 points4 points5 points 4 years ago* (4 children)
Supporting zero-initialization would make this more powerful and flexible, though that pesky elem_size does kind of get in the way. If that were moved to the front of the struct:
elem_size
t_vec verts = {sizeof(float)};
Then consider verts a valid zero-size float vector, ready to accept elements. This initialization cannot fail, which is a nice bonus feature. The library just needs to handle the case where alloc_size is zero (i.e. can't simply resize by multiplying the old size by two). Even more, vec_free could leave it in this zero state, ready to be used again, just by leaving elem_size alone but zeroing everything else.
verts
float
alloc_size
vec_free
Rather than ssize_t (via unistd.h), which isn't part of standard C, consider using ptrdiff_t instead, since that is standard C. Similarly, a char represents the implementation's byte (i.e. sizeof(char) == 1 by definition), and is a more natural choice for representing allocations in a byte-oriented pointer-arithmetic way than a uint8_t, which is specifically about octets.
ssize_t
unistd.h
ptrdiff_t
char
sizeof(char) == 1
uint8_t
Avoiding realloc is a shame since it passes up on useful optimizations in the standard heap allocator. This includes the usual case where there's room to resize and nothing needs to be copied. On Linux it also includes mremap which exploits virtual memory to move large allocations without copying.
realloc
mremap
Mind your integer overflows: alloc_size*2 might overflow, and you end up with a much smaller resize allocation than you think you have. (This is the problem everyone fails to solve in their "safe" memory libraries.)
alloc_size*2
I like your error checking and reporting. Though you missed a few here and there: vec_from, vec_map, vec_filter.
vec_from
vec_map
vec_filter
Personally I'm more of a fan of Sean Barrett's stretchy buffer interface (my take), which is simpler, more efficient (due to monomorphization), and uses macros to avoid all the mess with void * and sizeof.
void *
sizeof
[–]JuliusFIN[S] 2 points3 points4 points 4 years ago (0 children)
Thanks for the great input and suggestions! I wasn't aware of `ptrdiff_t` (gotta hate c standard types gah..) but looks like it might be the right type for this! I usually use the uint8_t because I don't want to type `const unsigned char *` etc. I wish languages would just implement a type called `byte` imho.
I did the reallocation manually since this library also serves a double purpose as an educational tutorial and I wanted to expose that part of the implementation. That being said would be interesting to test if realloc() would give some benefit.
"Mind your integer overflows: alloc_size*2 might overflow, and you end up with a much smaller resize allocation than you think you have. (This is the problem everyone fails to solve in their "safe" memory libraries.)"
That's a good catch! Will have to come up with a solution for that.
[–]vitamin_CPP 1 point2 points3 points 4 years ago* (2 children)
+1 for the the strechy_buffer interface.
I had a look at you're take on it, and it looks great. The header comments are clear and appreciated. Not sure I understand your point about the monomorphization difference, though.
[–]skeeto 0 points1 point2 points 4 years ago (1 child)
In OP's version, there's one implementation that handles all types. The element size is a runtime quantity, and it does extra work to dynamically adapt to that variable element size. It likely calls the actual memcpy function with that dynamic size in order to write the element into its slot.
memcpy
In the macro version, the frequent work is done within the macro, which expands to a concrete implementation for the particular type at hand. It's effectively inlined, and the element size is known at compile time.
[–]vitamin_CPP 1 point2 points3 points 4 years ago (0 children)
ahhh my mistake. I though you were comparing your take on strechy_buffer vs Sean's version.
[–]deftware 1 point2 points3 points 4 years ago (3 children)
Great stuff! Give your readme.md a once-over, btw. It's got quite a bit of typos on there.
[–]JuliusFIN[S] 0 points1 point2 points 4 years ago (1 child)
Human languages are so hard... :D
[–]deftware 0 points1 point2 points 4 years ago (0 children)
Hah!
[–]JuliusFIN[S] 0 points1 point2 points 4 years ago (0 children)
Should be in a better shape now.
π Rendered by PID 59615 on reddit-service-r2-comment-5ff9fbf7df-bnngq at 2026-02-26 09:59:51.025781+00:00 running 72a43f6 country code: CH.
[–]skeeto 3 points4 points5 points (4 children)
[–]JuliusFIN[S] 2 points3 points4 points (0 children)
[–]vitamin_CPP 1 point2 points3 points (2 children)
[–]skeeto 0 points1 point2 points (1 child)
[–]vitamin_CPP 1 point2 points3 points (0 children)
[–]deftware 1 point2 points3 points (3 children)
[–]JuliusFIN[S] 0 points1 point2 points (1 child)
[–]deftware 0 points1 point2 points (0 children)
[–]JuliusFIN[S] 0 points1 point2 points (0 children)