Are flexible array members really necessary?
Asked Answered
O

2

17

A struct with a flexible array member, apparently, is not intended to be declared, but rather used in conjunction with a pointer to that struct. When declaring a flexible array member, there must be at least one other member, and the flexible array member must be the last member in that struct.

Let's say I have one that looks like this:

struct example{
    int n;
    int flm[]; 
}

Then to use it, I'll have to declare a pointer and use malloc to reserve memory for the structure's contents.

struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int)); 

That is, if I want my flm[] array to hold five integers. Then, I can just use my struct like this:

ptr->flm[0] = 1; 

My question is, shouldn't I be able to just use a pointer instead of this? Not only would it be compatible pre-C99, but I could use it with or without a pointer to that struct. Considering I already have to use malloc with the flm, shouldn't I just be able to do this?

Consider this new definition of the example struct;

struct example{
    int n; 
    int *notflm; 
}

struct example test = {4, malloc(sizeof(int) * 5)}; 

I'd even be able to use the replacement the same way as the flexible array member:

Would this also work? (Provided the above definition of example with notflm)

struct example test; 
test.n = 4; 
notflm = malloc(sizeof(int) * 5); 
Oao answered 1/8, 2013 at 18:25 Comment(7)
possible duplicate of Flexible array members in C - bad?. Flexible arrays are part of C99; pointers are not and can have portability problems.Tibia
The main difference is that flexible array members allow you to allocate the entire structure in one contiguous block. If you want to copy the structure, just compute the size of it and do one copy. Whereas with a pointer, you have to copy the structure itself, but then also copy the inner array.Julide
@TedHopp in what conceivable way could you possibly be seriously saying that pointers are not part of C99?Signify
@ElchononEdelson - That was poorly phrased and should be interpreted in a very narrow context. I meant that using pointers at the end of a struct as a way of implementing flexible arrays is not part of the C99 standard (and can have portability problems, mainly due to hidden padding for array/pointer alignment).Tibia
@TedHopp I see. Yes, using a pointer at the end of a struct as a way of implementing flexible arrays would, indeed, be silly. (Using a pointer as the last element of a struct, and having it point to separately-allocated memory, however, is perfectly reasonable and happens all the time. It's just not the same thing as flexible arrays.)Signify
@ElchononEdelson - It may be silly (and dangerous), but it's been done all too often (which is what I think motivated the addition to the C99 standard). The temptation to ignore the dangers is that it avoids a second memory allocation (and the requisite clean-up) and also can eliminate one address indirection on each access. Unfortunately, the trick works on many architectures, so inexperienced programmers think it "just works".Tibia
Are we talking about different things here? To use a pointer at the end of a struct as a flexible array member, you'd have to have struct foo {int bar; int *ary;} *my; my=malloc(sizeof(struct foo)+10*sizeof(int)); my.ary=(int*)(my+1);, approximately. This is deeply stupid, of course. The other thing is to let the last element of the struct be a pointer to elsewhere, properly initialized with the address of a properly allocated chunk of memory. This does not constitute a struct with flexible array member, it's simply a different technique with its own different set of issues.Signify
J
31

Pointers are not arrays. The basic reasons for choosing which to use are the same as they always are with arrays versus pointers. In the special case of flexible array members, here are some reasons you may prefer them over a pointer:

  • Reducing storage requirements. A pointer will enlarge your structure by (typically) 4 or 8 bytes, and you'll spend much more in overhead if you allocate the pointed-to storage separately rather than with a single call to malloc.

  • Improving access efficiency. A flexible array member is located at a constant offset from the structure base. A pointer requires a separate dereference. This affects both number of instructions required to access it, and register pressure.

  • Atomicity of allocation success/failure. If you allocate the structure and allocate storage for it to point to as two separate steps, your code for cleaning up in the failure cases will be much uglier, since you have the case where one succeeded and the other failed. This can be avoided with some pointer arithmetic to carve both out of the same malloc request, but it's easy to get the logic wrong and invoke UB due to alignment issues.

  • Avoiding need for deep-copy. If you use a flexible array instead of a pointer, you can simply memcpy (not assign, since assignment can't know the flexible array length) to copy the structure rather than having to copy the pointed-to data too and fix up the pointer in the new copy.

  • Avoiding need for deep-free. It's very convenient and clean to be able to just free a single object rather than having to free pointed-to data too. This can also be achieved with the "carving up a single malloc" approach mentioned above, of course, but flexible arrays make it easier and less error-prone.

  • Surely many more reasons...

Jungly answered 1/8, 2013 at 18:35 Comment(1)
+1 nice list of reasons, I was looking for a nice summary of the benefits for this answer but I did not find anything really great, I am going to link to this answer as well b/c this is better than anything else I found so far.Mcneese
G
1

Those concepts are definitely not necessary as you have pointed out yourself.

The differences between the two that you have demonstrated are where your data is located in memory.

In the first example with flexible array your metadata and the array itself are in the same block of memory and can be moved as one block (pointer) if you have to.

In the second example your metadata is on the stack and your array is elsewhere on the heap. In order to move/copy it you will now need to move two blocks of memory and update the pointer in your metadata structure.

Generally flexible size arrays are used when you need to place an array and it's metadata spatially together in memory.

An example where this is definitely useful is for instance when placing an array with it's metadata in a file - you have only one continuous block of memory and each time you load it it will (most likely) be placed in a different location of your VM.

Gossett answered 1/8, 2013 at 18:36 Comment(1)
The array data could in many cases be located immediately following the structure data. That would allow the use of a single allocation, but any time the structure is loaded, moved, or copied, it would be necessary to update the pointer.Concertante

© 2022 - 2024 — McMap. All rights reserved.