stb库是否违反了C语言中的严格别名规则?

8xiog9wr  于 2023-01-25  发布在  其他
关注(0)|答案(1)|浏览(200)

我对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语言中有严格的别名规则。如果这确实避免了问题,我很想知道它为什么这样做,我可以自己合并它(也许少用几个宏)。

t5zmwmid

t5zmwmid1#

让我们看一下arrputhttps://github.com/nothings/stb/blob/master/stb_ds.h中的展开:

#define STBDS_REALLOC(c,p,s) realloc(p,s)

#define arrput      stbds_arrput

#define stbds_header(t)  ((stbds_array_header *) (t) - 1)

#define stbds_arrput(a,v)      (stbds_arrmaybegrow(a,1), (a)[stbds_header(a)->length++] = (v))

#define stbds_arrmaybegrow(a,n)  ((!(a) || stbds_header(a)->length + (n) > stbds_header(a)->capacity) \
                                  ? (stbds_arrgrow(a,n,0),0) : 0)

#define stbds_arrgrow(a,b,c)   ((a) = stbds_arrgrowf_wrapper((a), sizeof *(a), (b), (c)))

#define stbds_arrgrowf_wrapper            stbds_arrgrowf

void *stbds_arrgrowf(void *a, size_t elemsize, size_t addlen, size_t min_cap)
{
      ...
      b = STBDS_REALLOC(NULL, (a) ? stbds_header(a) : 0, elemsize * min_cap + sizeof(stbds_array_header));
      //if (num_prev < 65536) prev_allocs[num_prev++] = (int *) (char *) b;
      b = (char *) b + sizeof(stbds_array_header);
      if (a == NULL) {
        stbds_header(b)->length = 0;
        stbds_header(b)->hash_table = 0;
        stbds_header(b)->temp = 0;
      } else {
        STBDS_STATS(++stbds_array_grow);
      }
      stbds_header(b)->capacity = min_cap;

      return b;
}

这种类型的代码是如何不是未定义的行为 * 因为严格的别名 *
严格别名是关于 * 访问 * 与存储在那里的数据具有不同有效类型的数据。我认为 * 存储 * 在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 accesshttps://port70.net/~nsz/c/c11/n1570.html#6.5p6。
int *arraystbds_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_tvoid *ptrdiff_t成员,在任何常规体系结构中,它将具有良好对齐以访问其后的int(或任何其它常规类型)。
我只检查了arrput扩展中的代码。这是一个2000行代码,可能在任何地方都有其他未定义的行为。

相关问题