编译器如何处理错位?
How does the compiler handle the misalignment?
SO 问题 Does GCC's __attribute__((__packed__))
…? 提到 __attribute__((__packed__))
确实 "packing which introduces alignment issues when accessing the fields of a packed structure. The compiler will account for that when the the fields are accessed directly, but not when they are accessed via pointers"。
编译器如何确保直接访问字段?我想它在内部添加了一些填充或做了一些指针魔术。在下面的例子中,编译器如何确保 y
与指针相比被正确访问?
struct packet {
uint8_t x;
uint32_t y;
} __attribute__((packed));
int main ()
{
uint8_t bytes[5] = {1, 0, 0, 0, 2};
struct packet *p = (struct packet *)bytes;
// compiler handles misalignment because it knows that
// "struct packet" is packed
printf("y=%"PRIX32", ", ntohl(p->y));
// compiler does not handle misalignment - py does not inherit
// the packed attribute
uint32_t *py = &p->y;
printf("*py=%"PRIX32"\n", ntohl(*py));
return 0;
}
当编译器看到符号 p->y
时,它知道您正在访问一个结构成员,并且由于 p
的声明,该结构已被压缩。它将其转换为逐字节读取的代码,并执行必要的位移以将它们组合成一个 uint32_t
变量。本质上,它将表达式 p->y
视为类似于:
*((char*)p+3) << 24 + *((char*)p+2) << 16 + *((char*p)+1) << 8 + *(char*)p
但是当您通过 *py
间接访问时,编译器不知道该变量的值来自何处。它不知道它指向一个压缩结构,所以它需要执行这个移位。 py
被声明为指向 uint32_t
,通常可以使用一次读取整个 32 位字的指令访问它。但是这条指令期望指针与 4 字节边界对齐,因此当您尝试这样做时,您会由于未对齐而出现总线错误。
SO 问题 Does GCC's __attribute__((__packed__))
…? 提到 __attribute__((__packed__))
确实 "packing which introduces alignment issues when accessing the fields of a packed structure. The compiler will account for that when the the fields are accessed directly, but not when they are accessed via pointers"。
编译器如何确保直接访问字段?我想它在内部添加了一些填充或做了一些指针魔术。在下面的例子中,编译器如何确保 y
与指针相比被正确访问?
struct packet {
uint8_t x;
uint32_t y;
} __attribute__((packed));
int main ()
{
uint8_t bytes[5] = {1, 0, 0, 0, 2};
struct packet *p = (struct packet *)bytes;
// compiler handles misalignment because it knows that
// "struct packet" is packed
printf("y=%"PRIX32", ", ntohl(p->y));
// compiler does not handle misalignment - py does not inherit
// the packed attribute
uint32_t *py = &p->y;
printf("*py=%"PRIX32"\n", ntohl(*py));
return 0;
}
当编译器看到符号 p->y
时,它知道您正在访问一个结构成员,并且由于 p
的声明,该结构已被压缩。它将其转换为逐字节读取的代码,并执行必要的位移以将它们组合成一个 uint32_t
变量。本质上,它将表达式 p->y
视为类似于:
*((char*)p+3) << 24 + *((char*)p+2) << 16 + *((char*p)+1) << 8 + *(char*)p
但是当您通过 *py
间接访问时,编译器不知道该变量的值来自何处。它不知道它指向一个压缩结构,所以它需要执行这个移位。 py
被声明为指向 uint32_t
,通常可以使用一次读取整个 32 位字的指令访问它。但是这条指令期望指针与 4 字节边界对齐,因此当您尝试这样做时,您会由于未对齐而出现总线错误。