Question

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))
Was it helpful?

Solution

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.

OTHER TIPS

Work out how much memory you need and malloc that amount. For example if you want 9 more AudioBuffers then

list = malloc( sizeof *list + 9 * sizeof list->mBuffers[0] );

This entire construct is non-portable by the way (the behaviour is undefined if they access beyond the bound of mBuffers, which is 1), but it used to be reasonably common.

Note that you should not cast the value returned by malloc. There's no benefit to be gained by doing so, but there is harm that can be done.

There is a standard construct which is similar; if you remove the 1 from the definition of AudioBufferList (and malloc one more unit). This is called "flexible array member".

The preferred way to do this is to use the Apple-provided CAAudioBufferList::Create function from CAAudioBufferList.cpp in the Core Audio Utility Classes. You can download the sources here:

https://developer.apple.com/library/mac/samplecode/CoreAudioUtilityClasses/Introduction/Intro.html

Here is their implementation:

AudioBufferList*    CAAudioBufferList::Create(UInt32 inNumberBuffers)
{
    UInt32 theSize = CalculateByteSize(inNumberBuffers);
    AudioBufferList* theAnswer = static_cast<AudioBufferList*>(calloc(1, theSize));
    if(theAnswer != NULL)
    {
        theAnswer->mNumberBuffers = inNumberBuffers;
    }
    return theAnswer;
}

void    CAAudioBufferList::Destroy(AudioBufferList* inBufferList)
{
    free(inBufferList);
}

UInt32  CAAudioBufferList::CalculateByteSize(UInt32 inNumberBuffers)
{
    UInt32 theSize = SizeOf32(AudioBufferList) - SizeOf32(AudioBuffer);
    theSize += inNumberBuffers * SizeOf32(AudioBuffer);
    return theSize;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top