3

So, I am creating a structure that currently needs a lot of memory. I hope to reduce it in the future, but for now, it is what it is. Hence, I need to allocate some of its elements on the heap because I get a stack overflow if they are put on the stack. And yes, I increased the stack size but on the target platform I only have so much.

In this case, would it be 'better' to allocate every structure element on the heap, or put some on the stack and the big stuff on the heap? For instance:

typedef struct my_structure_s{ 

   int bounds[2];
   int num_values;
   int* values;               //needs to be very large

} my_structure_t;

Vs:

typedef struct my_structure_s{ 

   int* bounds;
   int* num_values;
   int* values;

} my_structure_t;

I know 'better' is largely subjective, and could quite possibly incite a riot here. So, what are the pros and cons of both examples? What do you usually do? Why?

Also, forgive the _s, _t stuff...I know some of you may find it in bad taste but that is the convention for the legacy codebase this will be integrated into.

Thanks everyone!

trincot
  • 317,000
  • 35
  • 244
  • 286
The Dude
  • 661
  • 2
  • 11
  • 20
  • The sizes of these structures are almost identical on every platform (and even completely identical on some platforms). – barak manos Dec 15 '14 at 17:18
  • How many of `my_structure_s` do you need? If your only constraint is `values` will be large, then what distinction are your trying to draw? Presumably you are allocating for `values` dynamically anyway, so whether you allocate the structure itself dynamically or not, your structure size itself will require no more than a couple dozen bytes at most. It makes little difference where you put the struct, unless the `20 odd` bytes it takes is a consideration. – David C. Rankin Dec 15 '14 at 17:27
  • Seems like you know there are issues, but are asking for a silver-bullet workaround. While sometimes we get lucky, it seems to me that replacing a few ints with a few pointers isn't going to buy you as much as you think. On 64 bit systems, it can take more space if not done with care. I would revisit the recursive part and see if it could be unrolled into a less stack-intensive technique. – Edwin Buck Dec 15 '14 at 17:35

2 Answers2

3

It is better to keep the simple members as direct values, and allocate just the array. Using the extra two pointers just slows down access for no benefit.

One other option to consider if you have C99 or C11 is to use a flexible array member (FAM).

You'd define your structure using the notation:

typedef struct my_structure_s
{ 
   int bounds[2];
   int num_values;
   int values[];
} my_structure_t;

You'd allocate enough memory for the structure and an N-element array in values all in a single operation, using:

my_structure_t *np = malloc(sizeof(*np) + N * sizeof(np->values[0]));

This then means you only have to free one block of memory to free.

You can find references to the 'struct hack' if you search. This notation is effectively the standardized form of the struct hack.


In comments, the discussion continued:

This is an interesting approach; however, I can't guarantee I will have C99.

If need be, you can use the 'struct hack' version of the code, which would look like:

typedef struct my_structure_s
{
    int bounds[2];
    int num_values;
    int values[1];
} my_structure_t;

The rest of the code remains unchanged. This uses slightly more memory (4-8 bytes more) than the FAM solution, and isn't strictly supported by the standard, but it was used extensively before the C99 standard so it is unlikely that a compiler would invalidate such code.

Okay, but how about:

typedef struct my_structure_s
{
    int bounds[2];
    int num_values;
    int values[MAX_SIZE];
} my_structure_t;

And then: my_structure_t *the_structure = malloc(sizeof(my_structure_t));

This will also give me a fixed block size on the heap right? (Except here, my block size will be bigger than it needs to be, in some instances, because I won't always get to MAX_SIZE).

If there is not too much wasted space on average, then the fixed-size array in the structure is simpler still. Further, it means that if the MAX_SIZE is not too huge, you can allocate on the stack or on the heap, whereas the FAM approach mandates dynamic (heap) allocation. The issue is whether the wasted space is enough of a problem, and what you do if MAX_SIZE isn't big enough after all. Otherwise, this is much the simplest approach; I simply assumed you'd already ruled it out.

Note that every one of the suggested solutions avoids the pointers to bounds and num_values suggested in option 2 in the question.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • This is an interesting approach, however, I can't guarantee I will have C99. For instance, C99 is worked around when it comes to complex numbers -- ie the codebase has its own homegrown support. – The Dude Dec 15 '14 at 17:44
0

do the first one. It is simpler and less error prone (you have to remember to allocate and release more things in the second one)

BTW - not that the first example will not put num_values on the stack. IT will go wherever you allocate the struct, stack, heap of static

pm100
  • 48,078
  • 23
  • 82
  • 145