For optimization reasons, I'd like arrays in struct to be "nullable", so that I can allocate memory for it later. Like array parameters in functions.
This means, I need an array inside a struct which should compile to a pointer and which will be assigned a dynamically allocated array later, but otherwise behaves like a "normal array" inside a struct
.
Example:
struct MyStruct
{
typeof(int (*)[4]) elements;
};
struct MyStruct myStruct;
int array[4];
myStruct.elements = array; // option 1
typeof(int[4]) *array2 = malloc(sizeof(myStruct.elements));
myStruct.elements = array2; // option 2
myStruct.elements[0] = 0xC;
myStruct.elements[1] = 0x0;
myStruct.elements[2] = 0xD;
myStruct.elements[3] = 0xE;
printf("%x %x %x %x\n", array2[0], array2[1], array2[2], array2[3]);
…
However, this doesn't work. It creates a pointer to an array so that myStruct.elements[0]
is the first array, not the first element of an array. And sizeof(myStruct.elements)
will be the size of a pointer.
Of course, we can try
struct MyStruct
{
int *elements;
};
but int *
is lacking type information compared to int [4]
which leads to weaker type checking. It would allow for
int bad_array[3];
myStruct.elements = bad_array;
sizeof
will be missing the information about the number of elements. IDEs also won't show the number of elements in IDE hints.
We could add an elements_count
member field, but doing so, doesn't allow for compile-time type checking. I only need the element count to be constant. Besides, an extra member field also uses additional memory.
As far as I know, the C programming language doesn't allow arrays which compile to a pointer and whose sizeof()
is the size of the array.
We can use a union
to come sufficiently close to the desired outcome.
struct MyStruct
{
union {
typeof(int [4]) *elements;
int *const element;
};
}
The constant element
pointer accesses single elements and also prevents pointer assignments which would have weaker type checking.
struct MyStruct myStruct;
int array[4] = { 0xC, 0x0, 0xD, 0xE };
myStruct.elements = &array; // okay
// int bad_array[3];
// myStruct.elements = &bad_array; // type error
printf("%x %x %x %x\n", myStruct.element[0], myStruct.element[1], myStruct.element[2], myStruct.element[3]);
The only drawback is, we need to dereference the pointer for sizeof
myStruct.elements = malloc(sizeof(*myStruct.elements));
I think, it does make sense, considering that malloc
allocates the memory of *myStruct.elements
and not the memory of the pointer myStruct.elements
. This is a common pointer allocation pattern.