我对Sean Barrett在C语言中为任何类型创建动态数组的技术很感兴趣。当前版本中的注解声称该代码可以安全地用于严格别名优化:https://github.com/nothings/stb/blob/master/stb_ds.h#L332
你可以这样使用它:
int *array = NULL;
arrput(array, 2);
arrput(array, 3);
它所做的分配同时包含数组数据和头结构:
typedef struct
{
size_t length;
size_t capacity;
void * hash_table;
ptrdiff_t temp;
} stbds_array_header;
宏/函数都将void* 带入数组,并通过强制转换void* 数组并向后移动一个数组来访问头部:
#define stbds_header(t) ((stbds_array_header *) (t) - 1)
我确信Sean Barrett比一般的程序员知识渊博得多。我只是很难理解这种类型的代码是如何不被定义的行为的,因为现代C语言中有严格的别名规则。如果这确实避免了问题,我很想知道它为什么这样做,我可以自己合并它(也许少用几个宏)。
1条答案
按热度按时间t5zmwmid1#
让我们看一下
arrput
在https://github.com/nothings/stb/blob/master/stb_ds.h中的展开:这种类型的代码是如何不是未定义的行为 * 因为严格的别名 *
严格别名是关于 * 访问 * 与存储在那里的数据具有不同有效类型的数据。我认为 * 存储 * 在
stbds_header(array)
* 指向的内存区域中的数据具有 *stbds_array_header
结构的有效类型,所以 * 访问 * 它是好的。结构成员由realloc
分配,并在stbds_arrgrowf
内由stbds_header(b)->length = 0;
行逐个初始化。这种类型的代码是如何不被定义的行为
我认为指针算法是好的。你可以说
realloc
的结果指向一个stbds_array_header
结构的数组。换句话说,当在stbds_arrgrowf
函数中执行第一个stbds_header(b)->length =
时,realloc
返回的内存“变成”一个stbds_array_header
结构的一个元素的数组,如www.example.com中的If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access
https://port70.net/~nsz/c/c11/n1570.html#6.5p6。int *array
在stbds_arrgrow
内被赋值以指向一个stbds_array_header
结构的“数组的最后一个元素之后一个”。(嗯,这也是int
数组开始的相同位置)。((stbds_array_header *) (array) - 1)
通过从“数组的最后一个元素的过去一”中减去一来计算最后一个数组元素的地址。无论如何,我会将其重写为(char *)(void *)t - sizeof(stbds_array_header)
,因为(stbds_array_header *) (array)
听起来像是会生成编译器警告。在
stbds_arrgrow
的扩展中将指向(char *)result_of_realloc + sizeof(stbds_array_header)
* 的指针分配给int *array
* 可能 * 在理论上潜在地不与int
数组类型正确对齐,从而使If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined
与www.example.com断开https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7。这在理论上非常重要,因为stbds_array_header
结构具有size_t
、void *
和ptrdiff_t
成员,在任何常规体系结构中,它将具有良好对齐以访问其后的int
(或任何其它常规类型)。我只检查了
arrput
扩展中的代码。这是一个2000行代码,可能在任何地方都有其他未定义的行为。