如何在不破坏严格别名的情况下在一个 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);
为可变大小的数组分配内存时,我经常这样做:
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);