I am making a stretchy-buffer-like single-file header library for queues in C.
I am passing the array as a void*
and the size of a single element to one of the functions:
void func(void* arr, size_t itemsize);
Inside func()
I need to do some pointer arithmetic with arr
.
I can't do arithmetic with void*
, so I thought I could create a data type of size itemsize
that I could cast arr
into, and thus solve my problem.
I saw the accepted this question, but would pointer arithmetic on char*[]
work the way I want? As in, (char*[5])(arr) + 3
is 15 bytes past arr
, and &((char*[4])(arr))[6]
points to 24 bytes past arr
.
I need to create the type inside func
.
We can look at how GLib does their arrays. And if this is for production code, just use GLib.
First, don't pass around the size and pointer. Make a struct which contains the size and the array and anything else. This allows you to pass around the complete array. Far simpler and less error prone. Here's how how GLib does it. The important bits are data
(guint8 is the GLib version of uint8 or 1 byte) and elt_size
(element size). The rest is gravy.
struct _GRealArray
{
guint8 *data;
guint len;
guint alloc;
guint elt_size;
guint zero_terminated : 1;
guint clear : 1;
gatomicrefcount ref_count;
GDestroyNotify clear_func;
};
We can look at g_array_insert_vals
to see how to insert values at an index. The meat of the insertion is here.
memcpy (g_array_elt_pos (array, index_), data, g_array_elt_len (array, len));
The special sauce is all in the g_array_elt_pos
(element position) and g_array_elt_len
(element size) macros. g_array_elt_pos(array, i)
replicates array[i]
.
#define g_array_elt_len(array,i) ((array)->elt_size * (i))
#define g_array_elt_pos(array,i) ((array)->data + g_array_elt_len((array),(i)))
The data is stored as 8 bit integers. g_array_elt_len
calculates the offset from the start of the array by multiplying the element size by the index. Then g_array_elt_pos
adds that to the start of the array.
That memcpy
is effectively...
memcpy(array[index_], data, (array->elt_size * len));
Copy len
units of array->elt_size
size from data
to array[i]
.
Looking up an element requires a type cast. GLib takes care of this for you with a macro. It returns a pointer to the element.
#define g_array_index(a,t,i) (((t*) (void *) (a)->data) [(i)])
For example.
// Similar to double num = array[5]
// Expands to ((double*) (void *) array->data)[5]
double num = g_array_index(array, double, 5);