Does flexible array members in C use bad practice?

I recently read that using flexible array members in C was bad software development practice. However, this statement was not supported by any argument. Is this a common fact?

( Flexible array elements is a C function introduced in C99 where you can declare the last element as an array of unspecified size. :)

struct header { size_t len; unsigned char data[]; }; 
+69
c arrays data-structures flexible-array-member
Oct 29 '08 at 14:21
source share
7 answers

It is a recognized fact that using goto is bad software development practice. This is not true. There are times when goto is useful, especially when cleaning up and migrating from assembler.

The elements of the flexible array amaze me with the fact that I use one main use, from the top of my head, which displays legacy data formats, such as window template formats in RiscOS. They would have been extremely useful for this about 15 years ago, and I’m sure that there are people who do things there that might find them useful.

Using flexible array elements is bad practice, I suggest we all tell the authors of the C99 specification. I suspect they may have a different answer.

+24
Oct 29 '08 at 14:36
source share

PLEASE READ CAREFULLY THE COMMENTS BELOW THE ANSWER

As C standardization advances, there is no longer any reason to use [1].

The reason I would refuse to do this is because you should not bind your code to C99 just to use this function.

The fact is that you can always use the following idiom:

 struct header { size_t len; unsigned char data[1]; }; 

It is fully portable. Then you can take into account 1 when allocating memory for n elements in the data array:

 ptr = malloc(sizeof(struct header) + (n-1)); 

If you already have C99 as a requirement for creating code for any other reason, or if you are targeting a specific compiler, I see no harm.

+18
Oct 29 '08 at 14:36
source share

You meant...

 struct header { size_t len; unsigned char data[]; }; 

In C, this is a common idiom. I think many compilers also accept:

  unsigned char data[0]; 

Yes, it is dangerous, but then again, it really is no more dangerous than regular C-arrays - that is, it is VERY dangerous ;-). Use it with caution and only in cases where you really need an array of unknown size. Make sure you malloc and free the memory correctly using something like: -

  foo = malloc(sizeof(header) + N * sizeof(data[0])); foo->len = N; 

An alternative is that the data is simply pointers to elements. Then you can translate the realloc () data to the desired size.

  struct header { size_t len; unsigned char *data; }; 

Of course, if you asked about C ++, any of them would be bad practice. Then you usually use STL vectors.

+10
Oct 29 '08 at 14:36
source share

I saw something like this: from the C interface and implementation.

  struct header { size_t len; unsigned char *data; }; struct header *p; p = malloc(sizeof(*p) + len + 1 ); p->data = (unsigned char*) (p + 1 ); // memory after p is mine! 

Note: data does not have to be the last.

+6
Jun 01. '10 at 9:30
source share

No, using flex array members in C is not bad practice.

This language feature was first standardized in ISO C99, 6.7.2.1 (16). For the current ISO C11 standard, it is specified in section 6.7.2.1 (18).

You can use them like this:

 struct Header { size_t d; long v[]; }; typedef struct Header Header; size_t n = 123; // can dynamically change during program execution // ... Header *h = malloc(sizeof(Header) + sizeof(long[n])); h->n = n; 

Alternatively, you can highlight it as follows:

 Header *h = malloc(sizeof *h + n * sizeof h->v[0]); 

Note that sizeof(Header) includes possible padding bytes, so the following distribution is incorrect and could lead to a buffer overflow:

 Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid! 

A structure with elements of a flexible array reduces the number of allocations for it by 1/2, i.e. Instead of 2 allocations for one structure object, you only need 1. That is, less effort and less memory occupied by the overhead of memory allocation. In addition, you save storage for one extra pointer. Thus, if you need to allocate a large number of such instances of the structure, you will noticeably improve the execution time and memory usage of your program (using a constant factor).

In contrast, using non-standardized constructions for flexible array elements that result in undefined behavior (for example, in long v[0]; or long v[1]; ) is obviously bad practice. Thus, like any vague behavior, this should be avoided.

Since the release of ISO C99 in 1999, almost 20 years ago, the pursuit of compatibility with ISO C89 has been a weak argument.

+6
Sep 16 '17 at 8:37
source share

As an additional note, for compatibility with C89, this structure should be distributed as follows:

 struct header *my_header = malloc(offsetof(struct header, data) + n * sizeof my_header->data); 

Or using macros:

 #define FLEXIBLE_SIZE SIZE_MAX /* or whatever maximum length for an array */ #define SIZEOF_FLEXIBLE(type, member, length) \ ( offsetof(type, member) + (length) * sizeof ((type *)0)->member[0] ) struct header { size_t len; unsigned char data[FLEXIBLE_SIZE]; }; ... size_t n = 123; struct header *my_header = malloc(SIZEOF_FLEXIBLE(struct header, data, n)); 

Setting FLEXIBLE_SIZE to SIZE_MAX almost guarantees that this will not work:

 struct header *my_header = malloc(sizeof *my_header); 
+4
May 20 '09 at
source share

There are some drawbacks to how structures are sometimes used, and this can be dangerous if you are not thinking about the consequences.

In your example, if you run the function:

 void test(void) { struct header; char *p = &header.data[0]; ... } 

Then the results are undefined (since no data storage has been allocated). This is something you usually know about, but there are times when C programmers are probably used to use value semantics for structures that break down in various ways.

For example, if I define:

 struct header2 { int len; char data[MAXLEN]; /* MAXLEN some appropriately large number */ } 

Then I can copy two instances simply by assignment, that is:

 struct header2 inst1 = inst2; 

Or, if they are defined as pointers:

 struct header2 *inst1 = *inst2; 

This, however, will not work, since the array of data variables is not copied. You want to dynamically allocate the size of the structure and copy over the array using memcpy or equivalent.

Similarly, writing a function that takes a structure will not work, since the arguments in the function calls are again copied by value, and therefore what you get is likely to be only the first element of your data array.

This does not make it a bad idea, but you should keep in mind always dynamically allocate these structures and pass them only as pointers.

+4
03 Mar. '14 at 9:31
source share



All Articles