- How will I dynamically allocate an array of
struct Registerwith each register having a dynamically allocated array ofstruct Field? - How will I access each of these members (Can I use
Register_A.FieldArray[23].High?)? How will I initialize register structure efficiently(assuming that register struct array is a big one) ? (Like a data segment without spending any compute)
struct Field { char High; char Low; char Attribute; }; struct Register { unsigned int ResetValue; struct Field FieldArray[]; };
4 Answers
I strongly discourage you from using a flex array, because they're nothing but a kludge. You should instead declare and use it as a struct Field *, mallocing it to size like any other dynamic array.
That said, to malloc with an array size of n_elem:
struct Register register = malloc(sizeof(*register) + n_elem * sizeof(*register->FieldArray));
To access the elements:
char high = register->FieldArray[0].High;
For initialization: in gcc, at least, you can initialize the array part statically like any other static array. I'm not sure how other compilers handle it.
Why flex arrays are discouraged:
I'll link this post, but I don't think the answer is complete (most of the discouragement is because it's c-99 only), so I'll add my thoughts.
They are an exception. In essence, you're declaring the array to be of zero size and accessing it out of bounds every time, just to an extent you have allocated for. You can't use a struct with a flexible array just like any other struct, e.g. you can't take the sizeof the flexible array. If you declare a static one or an array of them, you can't use the array member because there's no space allocated for it.
Example of using them in an array:
#include <stdio.h>
struct test {
int val;
int arr[];
};
int main() {
struct test tarr[2];
printf("%p\n%p\n", &tarr[0].arr[0], &tarr[1].val);
}
Output:
0x7fff59b67164
0x7fff59b67164
They are at the same address. If you try to write to the array member, you overwrite the next object. If you try to read from it, you read data from the next object. Depending on padding, this could even be a nonsense value from the padding.
They're like goto statements, both are potentially useful but more often a bad idea. If you don't know why they're dangerous, you shouldn't be using them (in real code; it's not a bad idea to play around with them in a test program to see how to use them correctly and how they can introduce problems).
5 Comments
A way to do it is by changing the size of the struct Register:
struct Register
{
unsigned int ResetValue;
struct Field FieldArray[1];
};
struct Register *Register_Create()
{
struct Register *reg = calloc(1, sizeof(struct Register) - sizeof(struct Field));
// check for NULL pointer
return reg;
}
struct Register *Register_SetSize(struct Register *reg, size_t size)
{
// check for NULL
reg = realloc(reg, sizeof(struct Register) + sizeof(struct Field) * (size - 1));
// check for NULL
return reg;
}
Here's an example on how to use this:
struct Register *reg = Register_Create();
reg = Register_SetSize(register, 5);
// You now have space for 5 elements in reg->FieldArray
reg->FieldArray[3].High = 'B';
Keep in mind that this solution only works if FieldArray is placed at the end of the struct.
Comments
Flexible array members are a part of C99.
- you can allocate a
strucwith flexible array member just as you think you should:malloc(sizeof(struct Register) + sizeof(struct Field[n])), wherenis the size you want. Don't forget to initialize the array and to keep track ofn, best by dedicating a member to that purpose - yes
BTW: the malloc above may be wasting some bytes due to padding, but usually it is not worth thinking about the exact formula. The minimal one has a max and uses offsetof.