I have implemented a simple dynamic vector library in C. It is a header-only library.
Full library listing -
void_vector.h:
#ifndef VOID_VECTOR_H
#define VOID_VECTOR_H
typedef enum {
VV_SUCCESS,
VV_MALLOC_FAILURE
} vv_err;
typedef struct vv {
size_t length;
size_t size;
void *data[];
} void_vector;
void_vector* new_void_vector(size_t size);
vv_err vv_push(void_vector **vv, void *el);
void *vv_pop(void_vector *vv);
const void* vv_read_index(void_vector *vv, size_t index);
void delete_void_vector(void_vector *vv, void (del_el)(void*));
#ifdef VOID_VECTOR_IMPL
#undef VOID_VECTOR_IMPL
#include <stdlib.h>
#define defualt_size 16ul
void_vector*
new_void_vector(size_t size)
{
if (!size) size = defualt_size;
void_vector* vv = malloc(sizeof(void_vector) + sizeof(void*) * size);
if (vv) {
vv->size = size;
vv->length = 0;
}
return vv;
}
vv_err
vv_push(void_vector **vv, void *el)
{
if ((*vv)->length >= (*vv)->size) {
void_vector *new_vv = realloc((*vv), sizeof(void_vector)
+ sizeof(void*) * (*vv)->size * 2);
if (!new_vv) return VV_MALLOC_FAILURE;
(*vv) = new_vv;
(*vv)->size *= 2;
}
(*vv)->data[(*vv)->length] = el;
(*vv)->length++;
return VV_SUCCESS;
}
void*
vv_pop(void_vector *vv)
{
if(vv->length == 0) return NULL;
vv->length--;
return vv->data[vv->length];
}
const void*
vv_read_index(void_vector *vv, size_t index)
{
if (index > vv->length) return NULL;
return (vv->data[index]);
}
void
delete_void_vector(void_vector *vv, void (del_el)(void*))
{
if (!del_el) del_el = &free;
for (int i = vv->length; i; i--) {
del_el(vv->data[i-1]);
}
free(vv);
}
#endif /* VOID_VECTOR_IMPL */
#endif /* VOID_VECTOR_H */
I am using this as test bench - void_vector_tb.c
#include <stdio.h>
#define VOID_VECTOR_IMPL
#include "void_vector.h"
int
main (int argc, char **argv)
{
void_vector* vv = new_void_vector(4);
char *strings[10];
for (int i = 0; i < 10; i++) {
strings[i] = malloc(32);
snprintf(strings[i], 32, "This is String: %d", i);
}
for (int i = 0; i < 10; i++) {
vv_push(&vv, strings[i]);
}
for (int i = 0; i < vv->length; i++) {
printf("%d:\t%s\n", i+1, vv_read_index(vv, i));
}
char *s;
while(s = (char*) vv_pop(vv)) {
printf("%s\n", s);
}
for (int i = 0; i < 10; i++) {
vv_push(&vv, strings[i]);
}
delete_void_vector(vv, NULL);
}
I am not using a make file. Compilation is performed by gcc -ggdb void_vector_tb.c -o void_vector_tb
I would greatly appreciate your time and any comments but I'm particularly interested in the following points:
- Am I doing anything dangerous in this?
- Is the code clear, what can I do to make it easier to follow?
- Is there an obvious way to greatly improve the efficiency?
- General style comments
- I'm not overly concerned about the contents of
void_vector_tb.c
.
I ran this through valgrind and got the following output:
==7009== HEAP SUMMARY:
==7009== in use at exit: 0 bytes in 0 blocks
==7009== total heap usage: 14 allocs, 14 frees, 1,616 bytes allocated
==7009==
==7009== All heap blocks were freed -- no leaks are possible
==7009==
==7009== For counts of detected and suppressed errors, rerun with: -v
==7009== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I am trying to minimize use of external libraries (although I don't think it would be possible to avoid stdlib.h).