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

ego*_*ego 18 c memory-management 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;
}
Run Code Online (Sandbox Code Playgroud)

然后我使用这样的arrray:

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;
}
Run Code Online (Sandbox Code Playgroud)

这工作和编译没有警告.然而,最近我读到了严格的别名.根据我的理解,上面的代码在严格别名方面是合法的,因为通过它访问int *的内存不是通过它访问的内存struct array *.代码实际上是否违反了严格的别名规则?如果是这样,如何修改它们不破坏它们?

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

dbu*_*ush 17

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

struct array {
    long length;
    int mem[];
};
Run Code Online (Sandbox Code Playgroud)

然后您可以像以前一样分配空间,而无需分配任何内容mem:

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

  • @ego你的问题没有具体说明.在这种情况下,您将不会有任何严格的别名违规,因为有问题的内存是malloc'ed并且还没有类型,但是您可能会遇到对齐问题. (3认同)
  • 仅当struct包含一个可变长度元素时,此方法才有效.如果有更多,那么据我所知,指针操作就像我概述的那样是必要的.关于严格的别名规则是否合法? (2认同)

Sto*_*ica 7

Modern C官方支持灵活的阵列成员.因此,您可以按如下方式定义结构:

struct array {
    long length;
    int mem[];
};
Run Code Online (Sandbox Code Playgroud)

并像现在一样分配它,而不会增加可疑指针操作的麻烦.它将开箱即用,所有访问将正确对齐,您不必担心语言的黑暗角落.虽然,自然,如果你有一个是唯一可行的,你需要分配这样的成员.

至于你现在拥有的,由于分配的存储没有声明的类型(它是一个空白的平板),你没有打破严格的别名,因为你没有给那个内存一个有效的类型.唯一的问题是可能会混淆对齐.虽然结构中的类型不太可能.

  • 而不是说"现代",你应该指定它自C99以来一直可用. (2认同)