A small header file that adds basic support for vectors in plain/pure ANSI-C (c89).
This header file is released under a (3-clause) BSD-Style Licence. Check either the vector.h or COPYING files for the complete licence.
The header is very simple to use - all you have to do is include it, and then use one macro to define the new vector type:
The example below is very basic and shows off 2 (not including the NEW_*
and DELETE_*
functions) functions/operations you can perform on the vector types.
#include "vector.h"
NEW_VECTOR_TYPE(IntVector, int) /* name the created vector type IntVector and make it's elements (the data it holds) of type int */
main()
{
/* create a new IntVector with space for 5 elements (i.e. 5 int's in this case) */
VECTOR_IntVector_v *vec1 = NEW_VECTOR_IntVector_v(5);
vec1.push(vec1, 10); /* 'Push' an element (the integer value 10) into the vector */
/* now vec1->Elements[0] == 10 */
/* do this a couple more times */
vec1->push(vec1, 20); /* now vec1->Elements[1] == 20 */
vec1->push(vec1, 30); /* now vec1->Elements[2] == 30 */
vec1->push(vec1, 40); /* now vec1->Elements[3] == 40 */
vec1->push(vec1, 50); /* now vec1->Elements[4] == 50 */
/* the vector will automatically resize itself so that it can hold the next element */
vec1->push(vec1, 60); /* now vec1->Elements[5] == 60 */
/* the vector will automatically resize itself so that it does not take up to much unnecessary memory */
vec1->pop(vec1); /* now vec1->Elements[5] is __Undefined Behaviour__ */
/* we are now done with the vector, delete to free up its resources */
DELETE_VECTOR_IntVector_v(vec1);
return (0);
}
To create a vector of any type, use the macro NEW_VECTOR_TYPE(name, elemType)
, where name
is the name you want to give to the vector type (so that the full name will be VECTOR_name_v
) and elemType
is the type of data the vector is supposed to hold (i.e. int's): NEW_VECTOR_TYPE(IntVector, int)
Because I was overcautious of namespace clashing! I highly doubt any sane person will ever give such pedantic names to typedef
's in their code - and that's why the names are so long.
I don't expect anyone to use the vectors this way (I certainly don't!) - instead I use a simple typedef
to alias the vector's name to something I know there will be no clashes with in my current project:
(continuing on from the previous example) typedef VECTOR_IntVector_v IntVec;
If you really want, you could even use the preprocessor to define the NEW_*
and DELETE_*
functions: #define NEW_IntVec NEW_VECTOR_IntVector_v
or #define NEW_IntVec(n) NEW_VECTOR_IntVector_v(n)
(The second define
looks more function like, but it has the same net effect on the code).
Hey, what gives! I thought c gave complete control to the programmer! I wish to shoot myself in the foot - how are the vectors implemented and can I mess with them in my code?
You're right! C does give complete control to the programmer, and so I thought it was best to give the user (read programmer) full access to the structure, instead of making it an opaque type. As such, you don't really have to use any of the function pointers provided by the vectors. The variables in the structure look like this:
struct __OBEJCT_SELF_ _self_ptr; /* DON'T EVER MODIFY/ACCESS THIS - YOU WILL NEVER NEED IT, BUT THE DELETE FUNCTIONS DO */
elemType *Elements; /* The elements in the vector */
size_t Occupied; /* The number of elements currently in the vector */
size_t Capacity; /* The maximum number of elements that the vector can hold */
/* function pointers */
...
I think the comments in the code explain all of that quite well :)
Sure! Just do this: VECTOR_IntVector_v vec1 = *NEW_VECTOR_IntVector_v(5);
(Dereferencing the pointer returned by NEW_VECTOR_name_v()
- It's completely safe I promise (the _self_ptr
member of the vector helps ensure that this is safe, and no memory is leaked when this sort of dereferencing is performed!).
I'm Glad you asked! The functions (Function Pointers / Methods) and their operations are listed below:
NOTE: ALL of the function pointers listed below expect a pointer to the vector to operate on as the first parameter!
elemType
refers to the second parameter passed to the NEW_VECTOR_TYPE()
macro, name
refers to the first parameter passed to the NEW_VECTOR_TYPE()
macro.
.front(VECTOR_name_v *vec)
.back(VECTOR_name_v *vec)
.get(VECTOR_name_v *vec, size_t n)
.size(VECTOR_name_v *vec)
Return the capacity of the vector (the amount of elements memory has been allocated for, explained below) - (return
type is size_t
):
.capacity(VECTOR_name_v *vec)
Resize the vector (vec->Elements)
to hold n
elements (you should never need this - but just incase) - (return
type is int
, 0 on success and -1 to indicate (realloc()
) failure):
.resize(VECTOR_name_v *vec, size_t n)
Resize the vector to exactly enough bytes for vec->Occupied
elements (so that vec->Occupied
== vec->Capacity
) - (return
type is int
, 0 on success and -1 to indicate (realloc()
) failure):
.clamp(VECTOR_name_v *vec)
Add one element (data
) to the end of the vector - (return
type is int
, 0 on success and -1 to indicate (realloc()
) failure):
.push(VECTOR_name_v *vec, elemType data)
Remove one element from the end of the vector - (return
type is int
, 0 on success and -1 to indicate (realloc()
) failure):
.pop(VECTOR_name_v *vec)
.data(VECTOR_name_v *vec)
.bytesize(VECTOR_name_v *vec)
Insert an element into position index
in the vector - (return
type is int
, 0 on success and -1 to indicate (realloc()
) failure)):
.insert(VECTOR_name_v *vec, size_t index, elemType data)
Remove the element in position index
in the vector - (return
type is int
, 0 on success and -1 to indicate (realloc()
) failure)):
.remove(VECTOR_name_v *vec, size_t index)
Good Question! There is quite a bit of overhead involved in calling malloc()
(and it's family, i.e. realloc()
) in succession, so like many vector implementations
this header chooses to double the amount of alloc
'd memory each time the vector's full capacity is reached - However, most vector implementations also "forget" to reduce the amount
of allocated memory when the size of the vector halfs, rest assured that vector.h
does not forget to reduce the memory when the vector's occupied
is half of it's capacity
.
Does this leak memory? Have you tested it with Valgrind!?
No, and Yes! No this header does not leak memory (providing that YOU call DELETE_VECTOR_name_v()
) and yes, it has been tested with Valgrind - It comes back squeaky clean!
YES! You can... I don't byte :) Open an issue on GitHub, send me an email or create a pull request!