1

Based on this answer: https://stackoverflow.com/a/19765782/1606345

#include <stdlib.h>

typedef struct {
    int *arr1;
    int *arr2;
} myStruct;

myStruct *allocMyStruct(int num)
{
    myStruct *p;

    if ((p = malloc(sizeof *p +
                 10 * sizeof *p->arr1 +
                 10 * num * sizeof *p->arr2)) != NULL)
    {
        p->arr1 = (int *)(p + 1);
        p->arr2 = p->arr1 + 10;
    }
    return p;
}

void initMyStruct(myStruct * a, int num)
{
    int i;

    for (i = 0; i < 10; i++) a->arr1[i] = 0;
    for (i = 0; i < 10 * num; i++) a->arr2[i] = -1;
}

int main (void)
{
    int num = 3;

    myStruct *a = allocMyStruct(num);
    initMyStruct(a, num);
    free(a);
    return 1;
}

It is safe to assign p->arr1 to the address of (p + 1)?

p->arr1 = (int *)(p + 1);
Community
  • 1
  • 1
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • 3
    You might be getting hit by memory alignment issues? – Chris O Nov 04 '13 at 13:01
  • 3
    In his case, no, this code looks "fine" – benjarobin Nov 04 '13 at 13:15
  • In theory there could be a problem with alignment, in practice it's fine to do this and it's a quite common optimization. – Art Nov 04 '13 at 13:31
  • 2
    @art: If it's quite common, I take it the `p+1`, in combination with the cast won't cause issues depending on the platform on which you compile this code? I mean: 32bit pointers take up 4 bytes, whereas 64bit ptrs require 8 bytes... That's what I'm unsure about, anyways... – Elias Van Ootegem Nov 04 '13 at 13:46
  • 1
    `p+1` gives you a pointer that has good enough alignment to address a myStruct (in this case). It's somewhere between hard and impossible to invent alignment requirements where the pointer to a struct has lower alignment restrictions than any of the struct members. So in that case it's pretty much impossible for the pointer to be misaligned for an int unless int has higher alignment requirements than an `int *` which I find hard to believe. There might be weird architectures out there where this can happen but I don't know of any. Theoretically possible? maybe. In practice? just fine. – Art Nov 04 '13 at 14:21
  • 1
    @Art: I've been digging around a bit. It does indeed seem very unlikely that `p+1` would cause trouble, though there's no real guarantee. `p+1` should give the next pointer, and so it shout _"shift"_ 4 or 8 bytes, depending on architecture. Although, there's no guarantee, but on _99.999... + IEEE 754 rounding error_ % of machines, this is not going to be a problem. – Elias Van Ootegem Nov 04 '13 at 14:49

1 Answers1

-1

You have a fundamental problem here in how you are thinking about struct allocation. When you malloc a struct, you malloc the sizeof that struct, you don't malloc for the arrays it will contain, those need to be allocated separately. To do this your code should look more like:

myStruct *allocMyStruct(int num)
{
    myStruct *p = malloc( sizeof( myStruct ) );

    if( p != NULL )
    {
        p->arr1 = malloc( sizeof( int ) * 10 ); // p->arr1 now points to an array of 10 elements
        p->arr2 = malloc( sizeof( int ) * 10 * num ); // p->arr2 now points to an array of 10 * num elements
    }
    return p;
}

Keep in mind when you free this you will need to free the arrays individually as well so if your pointer the myStruct was a:

free( a->arr1 );
free( a->arr2 );
free( a );
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 3
    The OP does allocate **enough** memory in this **one** call to `malloc()`. You might like to take a 2nd look. – alk Nov 04 '13 at 13:23
  • The OP is trying an optimization, where all memory are allocated in one piece instead of using separate allocation. The arr1/arr2 pointers are adjusted to point into that memory. That's a perfectly valid thing to do - but you'll have to care about alignment. – nos Nov 04 '13 at 13:25
  • @nos: Where exactly do you see the possible problem(s) with alignment? – alk Nov 04 '13 at 13:27
  • 2
    @alk in this case there is probably none - but in the general case you'd have to think about it. – nos Nov 04 '13 at 13:28
  • It's worth noting that there is "enough memory" allocated here. But because of the misbehavior the `free` will not work correctly. – Jonathan Mee Nov 04 '13 at 13:36
  • 1
    @JonathanMee: Which "*misbehaviour*" are you referring to please? Theres is exactly **one** allocation int the OP's code which's return value is passed to `free()` - this will work perfectly fine. – alk Nov 04 '13 at 13:48
  • Interesting, there isn't one... I thought that in his code the free would only clean up the size of the struct, but it appears that somewhere C keeps track of the size of memory allocated on the pointer. – Jonathan Mee Nov 04 '13 at 13:53
  • @JonathanMee: Of course C keeps track of how much memory was allocated [Check this answer](http://stackoverflow.com/questions/1518711/c-programming-how-does-free-know-how-much-to-free) – Elias Van Ootegem Nov 04 '13 at 14:00