我的记忆是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("%fn", *(float *)(w));
return 0;
}
如果这很重要,我正在使用GCC 9.3在x86 64位上编译
我的记忆是C代码,当解引用指针时,必须确保这些指针对你试图解引用的任何数据类型是对齐的。
C标准甚至没有定义尝试创建未对齐指针的行为,更不用说对其解引用了。C 2018 6.3.2.3 7说:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针没有为引用类型正确对齐,则行为是未定义的…
这就是为什么我很困惑为什么下面的测试代码只是工作。
当一段代码的行为没有定义时,你为什么要对它所做的任何事情感到困惑或惊讶呢?没有指定它将"工作",也没有指定它将不"工作"。
…这段代码是如何成功地打印浮点值在地址不是内存对齐的浮点访问?我正在使用GCC 9.3在x86 64位上编译
你在用什么开关?
对于-O3
, GCC完全消除了malloc
,memset
和memcpy
。它简单地计算出程序将把1.0f
传递给printf
,因此它直接执行该操作。对齐变得无关紧要。
对于-O0
, GCC将数据复制到未对齐的空间(使用movl
指令而不是memcpy
),然后使用movss
指令再次加载它。这是因为硬件支持它。编译器可能会对float
使用对齐,因为它更快更有效,但这并不意味着硬件不支持非对齐的加载和存储。
然而,即使硬件支持它,也不意味着你可以依赖它。在优化过程中,编译器可能会在数据未对齐时对对齐方式做出错误的假设。
对未正确对齐的指针解引用会触发未定义行为。
在x86/x64系统上,可能工作,但不能保证。其他架构可能会在同一个程序上触发错误。