问题
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
.
回答1:
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);
回答2:
I am confused by the last sentence of your question: however 20 not 15, 28 not 24. Ie base address + three scalars = fourth element of base zero array. Suggest define and declaring your array as a block of unsigned char rather than void. Next use union {} instead of type casting and pointer arithmetic. Alignment may be another issue. So instead of passing a base pointer and an element size, simply pass the base pointer and the byte size of the varying array, implying an implied element size and type. Or explicitly pass all three.
来源:https://stackoverflow.com/questions/63333480/create-data-type-of-n-size-bytes