Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to malloc a struct that contains a variable length array?

The CoreAudio framework uses a struct that is declared like this:

struct AudioBufferList
{
    UInt32      mNumberBuffers;
    AudioBuffer mBuffers[1]; // this is a variable length array of mNumberBuffers elements
};
typedef struct AudioBufferList  AudioBufferList;

As far as I can tell, this is basically a variable length collection of AudioBuffer structs. What is the 'correct' way to malloc such a struct?

AudioBufferList *list = (AudioBufferList *)malloc(sizeof(AudioBufferList));

Would this work?

I've seen all kinds of examples around the internet, like

calloc(1, offsetof(AudioBufferList, mBuffers) +
          (sizeof(AudioBuffer) * numBuffers))

or

malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (numBuffers - 1))
like image 868
Jawap Avatar asked Dec 08 '22 09:12

Jawap


1 Answers

That's not a variable length array; it's a 'struct hack'. The standard (since C99) technique uses a 'flexible array member', which would look this:

struct AudioBufferList
{
    UInt32      mNumberBuffers;
    AudioBuffer mBuffers[]; // flexible array member
};

One of the advantages of the FAM is that your questions are 'irrelevant'; the correct way to allocate the space for numBuffer elements in the mBuffers array is:

size_t n_bytes = sizeof(struct AudioBufferList) + numBuffer * sizeof(AudioBuffer);
struct AudioBufferList *bp = malloc(nbytes);

To answer your question, in practice both the malloc() and the calloc() will allocate at least enough space for the job, but nothing in any C standard guarantees that the code will work. Having said that, compiler writers know that the idiom is used and usually won't go out of their way to break it.

Unless space is incredibly tight, it might be simplest to use the same expression as would be used with a FAM; at worst, you have a little more space allocated than you absolutely need allocated. It will continue to work when you upgrade the code to use a FAM. The expression used in the calloc() version would also work with a FAM member; the expression used in the malloc() version would suddenly be allocating too little space.

like image 183
Jonathan Leffler Avatar answered Dec 28 '22 09:12

Jonathan Leffler