Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allocate memory for an array and a struct in one malloc call without breaking strict aliasing?

When allocating memory for a variable sized array, I often do something like this:

struct array {
    long length;
    int *mem;
};

struct array *alloc_array( long length)
{
    struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
    arr->length = length;
    arr->mem = (int *)(arr + 1); /* dubious pointer manipulation */
    return arr;
}

I then use the arrray like this:

int main()
{
    struct array *arr = alloc_array( 10);
    for( int i = 0; i < 10; i++)
        arr->mem[i] = i;
    /* do something more meaningful */
    free( arr);
    return 0;
}

This works and compiles without warnings. Recently however, I read about strict aliasing. To my understanding, the code above is legal with regard to strict aliasing, because the memory being accessed through the int * is not the memory being accessed through the struct array *. Does the code in fact break strict aliasing rules? If so, how can it be modified not to break them?

I am aware that I could allocate the struct and array separately, but then I would need to free them separately too, presumably in some sort of free_array function. That would mean that I have to know the type of the memory I am freeing when I free it, which would complicate code. It would also likely be slower. That is not what I am looking for.

like image 576
ego Avatar asked Feb 09 '18 21:02

ego


3 Answers

The proper way to declare a flexible array member in a struct is as follows:

struct array {
    long length;
    int mem[];
};

Then you can allocate the space as before without having to assign anything to mem:

struct array *alloc_array( long length)
{
    struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
    arr->length = length;
    return arr;
}
like image 104
dbush Avatar answered Nov 16 '22 06:11

dbush


Modern C officially supports flexible array members. So you can define your structure as follows:

struct array {
    long length;
    int mem[];
};

And allocate it as you do now, without the added hassle of dubious pointer manipulation. It will work out of the box, all the access will be properly aligned and you won't have to worry about dark corners of the language. Though, naturally, it's only viable if you have a single such member you need to allocate.

As for what you have now, since allocated storage doesn't have a declared type (it's a blank slate), you aren't breaking strict aliasing, since you haven't given that memory an effective type. The only issue is with possible mess-up of alignment. Though that's unlikely with the types in your structure.

like image 39
StoryTeller - Unslander Monica Avatar answered Nov 16 '22 06:11

StoryTeller - Unslander Monica


I believe the code as written does violate strict aliasing rules, when standard read in the strictest sense.

You are accessing an object of type int through a pointer to unrelated type array. I believe, that an easy way out would be to use starting address of the struct, and than convert it char*, and perform a pointer arithmetic on it. Example:

void* alloc = malloc(...);
array = alloc;
int* p_int = (char*)alloc + sizeof(array);
like image 28
SergeyA Avatar answered Nov 16 '22 05:11

SergeyA