Question

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);
Was it helpful?

Solution 2

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 )

OTHER TIPS

The technique that the C standard foresees for such a thing are flexible arrays, as was already mentionned:

typedef struct {
    size_t capacity;
    size_t len;
    unsigned char data[];
} dynarray_header;

If you allocate (or re-allocate) such a struct with enough space you may access the data element like any unsigned char array. char types may alias any other data type, so you wouldn't have problems with that.

If your compiler doesn't support flexible arrays, just put a [1] in there for data.

BTW, names starting with underscores are reserved in file scope, you are not supposed to use these.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top