8

I need to allocate several arrays of the same type and shape. At the beginning, I did something like:

void alloc_arrays_v1(size_t nmemb)
{
    int *a1, *a2, *a3;

    a1 = malloc(nmemb * sizeof int);
    a2 = malloc(nmemb * sizeof int);
    a3 = malloc(nmemb * sizeof int);

    /* do some stuff with the arrays */

    free(a1);
    free(a2);
    free(a3);
}

To avoid calling malloc and free several times, I changed the above into:

void alloc_arrays_v2(size_t nmemb)
{
    int *a, *a1, *a2, *a3;

    a = malloc(3 * nmemb * sizeof int);
    a1 = a;
    a2 = a1 + nmemb;
    a3 = a2 + nmemb;

    /* do some stuff */

    free(a);
}

This seems to be ok (in the sense that the functions behave the same way in the real-world case), but I wonder if this is still valid C code (undefined behaviour?), and if I can extend this method to complex data kind (arrays of structs, etc.).

michaelmeyer
  • 7,985
  • 7
  • 30
  • 36
  • 3
    Its entirely valid C code, and yes you can do it with other types including structures (pointer arithmetic just works if you let it, and you're letting it just fine). Run it in a debugger and do the math on the addresses starting with `a` to confirm this. – WhozCraig Jan 31 '14 at 08:26
  • @WhozCraig: I do this in the real-world case :) – michaelmeyer Jan 31 '14 at 08:33
  • 2
    Not sure what that has to do with debugging, as I debug *plenty* of real-world code. But its helpful to see it in action line by line, step by step. Don't think debuggers are helpful just to find bugs; they're equally fantastic tools for examining *valid* code and seeing how it works on the chosen platform. – WhozCraig Jan 31 '14 at 08:36
  • 1
    `sizeof int` -->, `sizeof(int)` – BLUEPIXY Jan 31 '14 at 08:43
  • @BLUEPIXY: size of is an operator and not a function. So do you need the parenthesis? – arunmoezhi Mar 14 '14 at 11:23
  • @arunmoezhi ; parentheses is required if type name. – BLUEPIXY Mar 14 '14 at 15:44
  • @arunmoezhi ; `sizeof` syntax : `sizeof unary-expression` , `sizeof ( type-name )` – BLUEPIXY Mar 14 '14 at 17:16

3 Answers3

3

Both are valid since you're using malloc to allocate contiguous memory. In fact code similar in form to the second case is often used when modelling matrices in C.

It's worth noting that

int a1, a2, a3, a4;
int* a = &a1;
int oops = *(a + 1);

is undefined behaviour, since you cannot expect the stack allocation to be contiguous.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
3

It is entirely valid in C. But remember to free only the a pointer. Your this method is similar to struct hack

However I think one logical problem in this code is that if you go out of bounds for a1 or a2 you will not be able to notice it as you will be accessing valid memory addresses, i.e. you will not get Seg Fault.
However, in first case you "may" get SegFault and notice your error.

0xF1
  • 6,046
  • 2
  • 27
  • 50
  • Thanks for the precisions. Also, does it remain valid for structures? – michaelmeyer Jan 31 '14 at 08:36
  • Array access beyond its max index is undefined behaviour - on this ground the [struct hack is UB](http://stackoverflow.com/q/3711233/183120), while the OPs case is in no way UB. Memory pools work this way. – legends2k Jan 31 '14 at 08:40
  • finishing the last sentence, Or worse, you may *not* get a segfault in the first snippet and thus *not* notice your error, which in many opinions (mine included) is far worse that crashing. rather it is a crash in-waiting, which is never fun. In both cases be careful with your pointer math boundaries and you'll be ok. – WhozCraig Jan 31 '14 at 08:43
  • @WhozCraig : Thanks for your humble opinion :-) I know out-of-bounds is a UB and hence getting Seg-Fault is not necessary, that's why I included "may". Anyways as you say and I find it right to be cautious yourself regarding out-of-bounds array access. – 0xF1 Jan 31 '14 at 08:51
2

It is perfectly valid.

What you do is essentially the same as this:

void alloc_arrays_v1(size_t nmemb)
{
    typedef int one_array[nmemb]; // this is one array
    typedef one_array three_arrays[3]; // this are three arrays

    one_array * a;
    int *a1, *a2, *a3;
    a = malloc(sizeof(three_arrays));

    a1 = a[0]; // a[0] is a one_array, which decays into an int * here.
    a2 = a[1]; // the same
    a3 = a[2]; // as above

    /* do some stuff with the arrays */

    free(a);
}

with the difference that the calculations are done by pointer and array arithmetics here.

glglgl
  • 89,107
  • 13
  • 149
  • 217