此代码是否违反了严格别名规则?

时间:2015-01-02 10:24:47

标签: c type-punning

我有一个大缓冲区,表示从HDD加载和解压缩的3D模型文件,该文件后面有一个标题和一些顶点,索引和子集数据。起初我以为我可以计算每个顶点/索引/子集数据开始的字节偏移,并简单地将其转换为兼容的指针类型并使用它,但这会破坏严格别名规则。那么一个解决方案是memcpy字节来分隔顶点/索引/子集数据数组(每个不同类型的c的数组)?

unsigned char *buf = NULL;
size_t offset = 0;

/* ... */

/* now @buf points to data immediately following the file header */

/* copy mesh subsets list */
memcpy(out->subsets, buf, sizeof(*out->subsets) * header.num_subsets);

/* copy vertex indices list */
offset = sizeof(*out->subsets) * header.num_subsets;
memcpy(out->indices, &buf[offset], sizeof(*out->indices) *
        header.num_indices);

/* copy mesh vertices list */
offset += sizeof(*out->indices) * header.num_indices;
memcpy(out->vertices, &buf[offset], sizeof(*out->vertices) *
        header.num_vertices);

2 个答案:

答案 0 :(得分:2)

您正在从错误的角度攻击严格的别名规则。将char数组投射到您的结构实际上是UB。这不仅是因为混叠,还因为对齐属性可能不同。不要那样做。

你必须反过来这样做:声明你想要拥有的真实类型的结构,然后使用指向该结构的void*char*指针来读取或者将数据复制到其中。

这始终有效:

  • 字符类型免于严格别名规则
  • 将对象的指针传递给函数(memcpy或其他)总是确保编译器在调用后不能对该对象的状态做出任何假设,因此他必须重新加载整个对象

编辑:也许有些混乱来自关于“别名规则”的奇怪的gcc警告。这只是通过指针转换类型惩罚产生的问题的一个方面。通常通过除字符类型之外的错误类型的指针访问对象可能具有未定义的行为。别名只是可能出错的几个方面之一。避免它。

答案 1 :(得分:2)

char*强制转换为指向结构的指针的问题不是严格别名规则:char类型免于严格别名规则。也就是说,您可以将与char数据一样的数据读取为任何其他类型,并且您可以反过来将任何数据读取为char数据。

演员表的问题是对齐。除非您直接从内存分配函数(确保为任何数据产生足够对齐的指针)获得char*,否则您可能会出现错位,从而导致程序崩溃。使用memcpy()解决此问题。但是,如果您可以确定char*完全对齐,则无需复制。

为避免混淆,这是完全合法的代码:

typedef struct Foo {
    ...
} Foo;

void bar() {
    char* buffer = malloc(sizeof(Foo));
    fillBuffer(buffer);
    Foo* header = (Foo*)buffer;    //Ok, buffer is a perfectly aligned pointer.
    readHeader(header);    //Ok, reading data written as char data does not violate strict aliasing rules.
}