如何在不破坏严格别名的情况下在一个 malloc 调用中为数组和结构分配内存?

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

为可变大小的数组分配内存时,我经常这样做:

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;
}

然后我像这样使用数组:

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;
}

这可以在没有警告的情况下运行和编译。然而最近,我读到了关于严格别名的内容。据我了解,上面的代码在严格别名方面是合法的,因为通过 int * 访问的内存不是通过 struct array * 访问的内存。代码实际上是否违反了严格的别名规则?如果是,如何修改才能不破坏它们?

我知道我可以单独分配结构和数组,但是我也需要单独释放它们,大概是在某种 free_array 函数中。这意味着我在释放内存时必须知道要释放的内存类型,这会使代码复杂化。它也可能会更慢。那不是我要找的。

在结构中声明灵活数组成员的正确方法如下:

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

然后你可以像以前一样分配 space 而不必分配任何东西给 mem:

struct array *alloc_array( long length)
{
    struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
    arr->length = length;
    return arr;
}

现代 C 正式支持 flexible array members。所以你可以定义你的结构如下:

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

并像现在一样分配它,而不会增加可疑指针操作的麻烦。它开箱即用,所有访问都将正确对齐,您不必担心语言的暗角。当然,只有当您需要分配一个 单个 这样的成员时,它才可行。

至于你现在拥有的,因为分配的存储没有声明的类型(它是一张白纸),你没有打破严格的别名,因为你没有给那个内存一个有效的类型。唯一的问题是可能出现对齐混乱。尽管对于您的结构中的类型来说这不太可能。

我相信所写的代码确实违反了严格的别名规则,从最严格的意义上来说是标准阅读。

您正在通过指向不相关类型 array 的指针访问类型 int 的对象。我相信,一个简单的方法是使用结构的起始地址,而不是将其转换为 char*,然后对其执行指针运算。示例:

void* alloc = malloc(...);
array = alloc;
int* p_int = (char*)alloc + sizeof(array);