x86比较宽容,ARM64严格 未对齐访问会崩,安全做法一般是保证对齐或用 memcpy。
uint64_t *ptr = (uint64_t*)(buf+1); *ptr = 0x1234;
这种奇怪对齐的写入在x86下通常没问题。struct Foo {
char a;
uint64_t b;
};
Foo* p = (Foo*)malloc(sizeof(Foo));
p->b = 123;
// 这里 ARM64 上可能报 Bus Error原因是,b在内存中可能没对齐到8字节边界。x86容忍,但ARM64不行。
另一个常见场景是 char 或 uint8_t 偏移访问
char buf[16];
uint64_t* p = (uint64_t*)(buf + 1);
*p = 0x12345678; // ARM64会报错C++11提供alignas
alignas(8) uint64_t value;或者对结构体:
struct alignas(8) Foo {
char a;
uint64_t b;
};void* ptr;
posix_memalign(&ptr, 8, sizeof(Foo));
Foo* p = (Foo*)ptr;如果需要从 char* buffer 读取 uint64_t 用 memcpy
uint64_t value;
memcpy(&value, buf + 1, sizeof(value));这样不会触发 Bus Error,也不会依赖对齐。
uint64_t* 直接读写
#pragma pack 很容易导致Bus Errorchar buf[16];
uint64_t* p = (uint64_t*)(buf + 1); // x86 OK, ARM64 崩
*p = 0x12345678;安全的写法
uint64_t value = 0x12345678;
memcpy(buf + 1, &value, sizeof(value));char* raw = (char*)malloc(13);
uint64_t* p = (uint64_t*)raw; // ARM64 崩安全的写法
uint64_t* p;
posix_memalign((void**)&p, 8, sizeof(uint64_t));
*p = 0x12345678;
// 直接分配8的整数字节
char* raw = (char*)malloc(16);
uint64_t* p = (uint64_t*)raw;#pragma pack(1)
struct Foo {
char a;
uint64_t b;
};
Foo f;
f.b = 42; // ARM64 崩安全做法
char buf[8];
uint64_t val = *(uint64_t*)buf; // 未对齐 → ARM64 崩安全做法
uint64_t val;
memcpy(&val, buf, sizeof(val));数组大小是8字节,不代表数组起始地址一定是8字节对齐。
你需要区分:
这是两回事
char buf[8];
printf("%p\n", (void*)buf);
// 可能看到的地址是 0x1000 8字节对齐
// 也可能是 0x1001 未对齐
// 也可能是 0x1004 4字节对齐不过对于
char buf[8];
uint64_t val = *(uint64_t*)buf;实际上大部分编译器会把buf放在栈上,而栈本身通常16字节对齐的。所以很多时候
(char*)buf 恰好是8字节对齐的。 通常在
x86和ARM64上通常都不会崩。真正危险的是
char buf[16];
uint64_t val = *(uint64_t*)(buf+1);不要把任意 char 当成
uint32_t、uint64_t*、struct*
来解引用。
能用 memcpy 就不用强制转换,结构体按自然对齐,buffer 偏移读写小心。x86 可以侥幸,但 ARM64 不会放过你。
“在 x86 跑了十年,一上 ARM 就炸”的代码。人类总喜欢把运气误认为技术。