views:

685

answers:

4

This is a fairly basic question, which for some reason, a proper solution escapes me at the moment. I am dealing with a 3rd-party SDK which declares the following structure:

struct VstEvents
{
    VstInt32 numEvents;  ///< number of Events in array
    VstIntPtr reserved;  ///< zero (Reserved for future use)
    VstEvent* events[2]; ///< event pointer array, variable size
};

Even though this is a "variable sized" array, it's declared statically. So obviously, if I make a VstEvents object, set the numEvents to something, and then go through and start adding them to the array, it's going to cause memory corruption.

So how am I supposed to properly deal with a structure like this? Should I allocate my own VstEvent* array and then point events[0] to it?

A: 

If you know how many there are you can allocate it with

struct VstEvents *evnts;

evnts = (struct VstEvents*)malloc(sizeof(struct VstEvents) + 
                                  numEvents*sizeof(VstEvent*));

This will allocate 2 extra slots

epatel
In C, one should not cast the return value from malloc(). Doing so can hide an error.
unwind
That's only true in C89. In C99, you can't use implicit function declarations, so it makes no difference whether or not you cast. Casting makes the code compilable under C++.
Adam Rosenfield
FWIW, SDX2000's example doesn't waste memory by allocating two extra pointer slots.
Adisak
A: 

What the structure is declaring is an array (of size 2) of pointers to VstEvent objects. You should allocate a VstEvent and assign it to events[0], setting numEvents to 1. If you have 2 VstEvents, then allocate another VstEvent, assign it to events[1], and set numEvents to 2. If you need more than 2, then you'll need to do a realloc on VstEvents to increase its size so that events contains the number of pointers you need and assign to 2, 3, 4, ... as needed. Or, if you know ahead of time how many events you will need to store, you could allocate it initially as @SDX2000 shows.

I think the basic idea is that they declare it with size 2 for convenience so that if you only have 1 or 2 you don't need to do an additional malloc to size the events array.

tvanfosson
Is it possible to cleanly assign to the events field? It's an array, those are const. I think you're wrong, and that @epatel is correct.
unwind
You're correct. I'll edit.
tvanfosson
+3  A: 

you need to pre allocate a chunk of memory big enough to contain the whole thing + any additional records you want...

struct VstEvents
{
 VstInt32 numEvents;  ///< number of Events in array
 VstIntPtr reserved;  ///< zero (Reserved for future use)
 VstEvent* events[2]; ///< event pointer array, variable size
};

#define numEventsRequired 10
VstEvents *vstEvents = (VstEvents*)malloc(sizeof(VstEvents) + sizeof(VstEvents*)*(numEventsRequired-2));

vstEvents->numEvents = numEventsRequired;
SDX2000
A: 

Your third-party library is a bit odd. You need to distinguish between two sorts of "variable-sized arrays"

  • The size is known when the array is allocated and it never changes thereafter. This is the easy case and I show a C99 idiom below.

  • The eventual size is unknown when the array is allocated and the array may need to grow during its lifetime. For something like this I'd recommend you check out the implementation of Seq_T in source file seq.c in Dave Hanson's C Interfaces and Implementations. You can adapt his code fairly easily.

If you know the size at allocation time, the C99 idiom is this:

struct VstEvents
{
    VstInt32 numEvents;  ///< number of Events in array
    VstIntPtr reserved;  ///< zero (Reserved for future use)
    VstEvent* events[];  ///< event pointer array, variable size
};


struct VstEvents *alloc_vst_events(int num_events) {
  struct VstEvents *p = 
            malloc(sizeof(*p) + num_events * sizeof(p->events[0]));
  return p;
}

The key is that weird array in the struct with size left unspecified. Requires C99.

Since you're stuck with the 3rd party weirdness I would try this:

#define NELEMS(A) (sizeof(A) / sizeof((A)[0]))

struct VstEvents *alloc_vst_events(int num_events) {
  struct VstEvents *p;
  int events_needed = num_events - NELEMS(p->events);
  p = malloc(sizeof(*p) + events_needed * sizeof(p->events[0]));
  return p;
}

Disclaimer: I have not pushed this code through a compiler. Corrections welcomed.

Norman Ramsey