0

I have the following code:

typedef struct
{
   int name;
   int info[1]; 
} Data;

then I have five variables:

int a, b, c, d, e;

how can I use this as a flexible array to keep all the values of the five variables?

ctn
  • 2,887
  • 13
  • 23
user2131316
  • 3,111
  • 12
  • 39
  • 53
  • 1
    what do you mean by _flexible_ here ? – VoidPointer Jun 27 '13 at 13:41
  • 1
    What is a "flexible array"? The struct you showed there isn't an array at all. Your question isn't clear in the least. – Nathan Fellman Jun 27 '13 at 13:41
  • 4
    It is very obvious that he means a _flexible array member_. If you don't know what that is, well, then don't bother answering. – Lundin Jun 27 '13 at 13:42
  • 1
    Some people use structures like these and allocate more memory for the entire struct, so that the struct and the array will be a single, contiguous allocation: `Data *d = malloc(sizeof(Data) + 10*sizeof(int));` – ctn Jun 27 '13 at 13:44
  • Why the downvote, why the vote-to-close as off-topic? – alk Jun 27 '13 at 13:46
  • 1
    @ctn It is not just "some people". Flexible array members is a language feature which has been part of the C language for 14 years. Even before C99, it was commonly used (often called "struct hack" or similar), but it was then undefined behavior and unsafe. – Lundin Jun 27 '13 at 13:48
  • -1 *This question does not show any research effort...* – Caleb Jun 27 '13 at 13:49
  • possible duplicate of [Allocating struct with flexible array member](http://stackoverflow.com/questions/12680946/allocating-struct-with-flexible-array-member) – Caleb Jun 27 '13 at 13:52

3 Answers3

7

To do this properly, you should declare the flexible array member as an incomplete type:

typedef struct
{
   int name;
   int info[]; 
} Data;

Then allocate memory for it dynamically with

Data* data = malloc(sizeof(Data) + sizeof(int[N]));

for(int i=0; i<N; i++)
{
  data->info[i] = something; // now use it just as any other array
}

EDIT

Ensure that you are using a C99 compiler for this to work, otherwise you will encounter various problems:

If you allocate an array of length 1, then you will malloc 1 item for the first element of the array together with the struct, and then append N bytes after that. Meaning you are actually allocating N+1 bytes. This is perhaps not what one intended to do, and it makes things needlessly complicated.

(To solve the above problem, GCC had a pre-C99 extension that allowed zero-length arrays, which isn't allowed in standard C.)

Pre-C99, or in any other context than as a flexible array member, C doesn't allow incomplete array types as the one shown in my code.

C99 guarantees that your program is well-defined when using a flexible array member. If you don't use C99, then the compiler might append "struct padding" bytes between the other struct members and the array at the end. Meaning that data->info[0] could point at a struct padding byte and not at the first item in your allocated array. This can cause all kinds of weird, unexpected behavior.

This is why flexible array members were called "struct hack" before C99. They weren't reliable, just a dirty hack which may or may not work.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • The reason people sometimes use `int info[1]` is to avoid depending on C99. Seems like depending on C99 isn't a bad idea at this point, but you mention it yourself so it seems to still be a concern. More info at [this answer](http://stackoverflow.com/a/247040/643383). – Caleb Jun 27 '13 at 14:00
  • @Caleb There are many problems when you aren't using a C99 compiler. I'll update the answer with more info. – Lundin Jun 27 '13 at 14:02
2

That kind of structure is a somewhat common idiom in C; the idea is that you allocate extra space at the end of the struct, where the elements of info after the first are actually stored. The size-1 array member at the end of the struct then allows you to use array syntax to access this data.

If you want to store 5 elements you'll have to do:

Data * data=malloc(sizeof(Data)+sizeof(int)*4); /* 4 because the first element is
                                                already included in the size of
                                                the struct */
/* error checking omitted ... */
data->info[0]=a;
data->info[1]=b;
data->info[2]=c;
data->info[3]=d;
data->info[4]=e;
/* ... */
/* when you don't need d anymore remember to deallocate */
free(data);

You may also write a helper function to ease the allocation:

Data * AllocateData(size_t elements)
{
    if(elements==0)
        return NULL;
    return malloc(sizeof(Data)+sizeof(int)*(elements-1));
}

and the example above would be

Data * data=AllocateData(5);
/* then as above */
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
2

This is called flexible arrays and was introduced in C99. Often called a struct hack too. In C99, the flexible array member should be declared without a size.

You need to dynamically allocate memory that can hold more memory than the size of the struct. As the array is the last member in the struct, you can index it past its size, provided you allocated enough memory for it.

    typedef struct
    {
       int name;
       int info[1]; 
    } Data;

Data *d = malloc(sizeof(*d) + (5 * sizeof(int)); //enough for the struct and 5 more ints.
//we have enough room for 6 elements in the info array now
//since the struct has room for 1 element, and we allocated room for another 5 ints
d->info[0] = 1;
d->info[1] = 2;
d->info[2] = 3;
d->info[3] = 4;
d->info[4] = 5;
d->info[5] = 6; 

Using an array member with 1 size int info[1]; in this manner is technically undefined behavior - but will work fine on many popular compilers. With a C99 compiler this is supported by a flexible array member declared as int info[];. Read more here

nos
  • 223,662
  • 58
  • 417
  • 506