我写了一个这样的动态数组:
#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;
}
可以通过[]
操作来访问动态阵列。调整大小时,我可以使用__dynarray_header*)array - 1
来检索容量和长度信息。
这个想法在小测试中有效。然而,GCC警告要打破严格的混叠。
我还发现一些更大的项目segfault没有-fno-strict-aliasing
编译器选项(带有-O3
优化)。
我知道什么是严格混叠,以及为什么我的代码会破坏严格混叠。
我的问题是:有没有比我上面展示的方法更好的方法来实现一个同时支持[]
操作和动态调整大小的动态数组?
额外:
使用此动态阵列的演示程序:
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);
C标准预见到的这种技术是灵活的阵列,正如已经提到的:
typedef struct {
size_t capacity;
size_t len;
unsigned char data[];
} dynarray_header;
如果分配(或重新分配)这样一个具有足够空间的struct
,则可以像访问任何unsigned char
数组一样访问data
元素。char
类型可以对任何其他数据类型进行别名,因此您不会有问题。
如果您的编译器不支持灵活的数组,只需为data
放入一个[1]
即可。
顺便说一句,以下划线开头的名称在文件范围内是保留的,您不应该使用这些名称。
-fstrict-aliasing
提供的主要优化是,在大多数情况下,对foo *
的引用可以任意移动到对bar *
的引用之后。您看到的segfault很可能是由于某个引用被移过某个free
类型的操作。
虽然这感觉有点脏,但您可以通过在结构中添加预期数组元素类型的并集,使其在C89下工作,例如:
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;
然后,不返回header + 1
,而是返回(void *)&(header->array_head)
。
现在,即使使用了严格的别名,编译器也更有可能将指向__dynarray_header
的指针视为指向该并集中任何对象的指针的别名,除非这些指针也是restrict
限定的。(我假设对于您的用例,它们不是,至少在触发seg错误的上下文中是这样。)
不过。。。正如Dennis Ritchie所说,这似乎是"与实现之间毫无根据的亲密关系"。或者,换句话说,就是一次黑客攻击。祝你好运
(编辑:正如上面卡尔提醒我的那样,在C99中,你可以使用灵活的数组成员。我没有使用它们,只是因为在我使用的C编译器中,C99支持似乎不是默认的。以下是IBM的参考:http://pic.dhe.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Frzarg%2Fflexible.htm)