指针类型之间的转换产生错误对齐的结果

由于指针对齐不正确,以下可能有未定义的行为:

 char *memory_block = calloc(sizeof(uint32_t) + 1, 1);
 uint32_t *intptr = (uint32_t*)(memory_block + 1);  /* possible undefined behavior */
 uint32_t mvalue = *intptr;

指针转换时会发生未定义的行为。根据 C11,如果两个指针类型之间转换产生错误对齐的结果(6.3.2.3),则行为未定义。例如,uint32_t 可能需要对齐 2 或 4。

另一方面,calloc 需要返回一个适合任何对象类型的指针; 因此,memory_block 正确对齐,在其初始部分包含一个 uint32_t。然后,在 uint32_t 需要对齐 2 或 4 的系统上,memory_block + 1 将是奇数地址,因此没有正确对齐。

观察到 C 标准请求已经定义了强制转换操作。这是因为在地址被分段的平台上,字节地址 memory_block + 1 甚至可能不具有作为整数指针的适当表示。

char *转换为指向其他类型的指针而不考虑对齐要求有时会错误地用于解码打包结构,例如文件头或网络包。

你可以使用 memcpy 避免因未对齐的指针转换而产生的未定义行为:

memcpy(&mvalue, memory_block + 1, sizeof mvalue);

这里没有指向 uint32_t*的指针转换,并且逐个复制字节。

我们示例的此复制操作仅导致 mvalue 的有效值,因为:

  • 我们使用了 calloc,因此字节被正确初始化。在我们的例子中,所有字节都有值 0,但任何其他正确的初始化都可以。
  • uint32_t 是一种精确的宽度类型,没有填充位
  • 任意位模式都是任何无符号类型的有效表示。