Search code examples
cmallocdynamic-arraysstrict-aliasing

void* type cast breaks strict -aliasing?


I wrote a dynamic array like this:

#include <stdlib.h>

typedef struct {
    size_t capacity;
    size_t len;
} __dynarray_header;

void* dynarray_new() {
    __dynarray_header* header = malloc(sizeof(__dynarray_header));
    header->capacity = 0;
    header->len = 0;
    return header + 1;
}

The dynamic array can be accessed with a [] operation. When resizing, I can use __dynarray_header*)array - 1 to retrieve capacity and length information.

The idea works in small tests. However, GCC warns about breaking strict-aliasing.

I've also found some larger projects segfault without the -fno-strict-aliasing compiler option (with -O3 optimization).

I know what strict-aliasing is, and why my code breaks strict-aliasing.

My question is: Is there a better way to implement a dynamic array supporting both the [] operation and dynamic resizing than the one I showed above?

Extra:

A demo program using this dynamic array:

int* arr = dynarray_new();
arr = dynarray_resize(sizeof(int) * 2);
arr[0] = 1;
arr[1] = 2;
arr = dynarray_resize(sizeof(int) * 4);
arr[2] = 3;
arr[3] = 4;
dynarray_free(arr);

Solution

  • The main optimization afforded by -fstrict-aliasing is that references to foo * can be arbitrarily moved past references to bar * in most circumstances. The segfaults you see are likely due to a reference getting moved past a free type operation somewhere.

    While this feels a little dirty, you may be able to make it work under C89 by adding a union of prospective array element types into your structure, such as:

    typedef struct {
        size_t capacity;
        size_t len;
        union {
            int i;
            double d;
            my_type mt;
            etc e;
            /* add additional types here. */
        } array_head;
    } __dynarray_header;
    

    Then, instead of returning header + 1, return (void *)&(header->array_head).

    Now, even with strict aliasing, the compiler is more likely to consider a pointer to __dynarray_header to possibly alias a pointer to anything in that union, unless the pointers are also restrict-qualified. (I'm assuming for your use case, they are not, at least in the contexts that trigger seg-faults.)

    Still... as Dennis Ritchie said, it seems like "unwarranted chumminess with the implementation." Or, in other words, a hack. Good luck!

    (Edit: As Carl above reminded me, in C99 you can use flexible array members. I haven't used them, simply because C99 support doesn't seem to be the default in the C compilers I use. Here's IBM's reference: http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Frzarg%2Fflexible.htm )