C内存对齐指针取消引用

C memory alignment pointer dereference

我的回忆是,当取消引用指针时,C 代码必须确保这些指针与您尝试取消引用的任何数据类型对齐。这就是为什么我对以下测试代码为何正常工作感到困惑。浮点数应该从 % 4 寻址开始,那么这段代码如何能够成功地在未针对浮点访问进行内存对齐的地址处打印浮点值?

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main()
{
  float x = 1.0f;
  void *q = malloc(32);
  memset(q, 0, 32);
  char *w = (char *)q + 3;
  memcpy(w, &x, sizeof(float));
  printf("%f\n", *(float *)(w));
  return 0;
}

以防万一,我正在使用 GCC 9.3 在 x86 64 位上进行编译

取消引用未正确对齐的指针会触发未定义的行为。

在 x86/x64 系统上,它 可能 工作,但不能保证。其他架构可能会在同一程序上触发错误。

My recollection is that C code, when dereferencing pointers, must make sure that those pointers are aligned for whatever data type you are trying to dereference.

C 标准甚至没有定义尝试创建未对齐指针的行为,更不用说解除引用了。 C 2018 6.3.2.3 7 说:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined…

That is why i'm confused why the following test code is just working.

当一段代码的行为未定义时,您为什么会对它所做的任何事情感到困惑或惊讶?没有指定它会“工作”,也没有指定它不会“工作”。

… how is this code successfully able to print the float value at address that is not memory aligned for float access? … I'm compiling on x86 64 bit using GCC 9.3

你用的是什么开关?

With -O3,GCC完全消除了mallocmemsetmemcpy。它只是计算出程序会将 1.0f 传递给 printf,因此它会直接执行此操作。对齐变得无关紧要。

With -O0,GCC 确实将数据复制到未对齐的 space(使用 movl 指令而不是 memcpy),然后使用 [= 再次加载它19=] 指令。这是可行的,因为硬件支持它。编译器可能对 float 使用对齐,因为它更快、更高效,但这并不意味着硬件不支持未对齐的加载和存储。

但是,即使硬件支持,也不代表可以信赖。在优化期间,编译器可能会做出有关对齐的假设,但当数据未对齐时该假设会失败。