我注意到一些非常奇怪的事情。假设我定义了以下结构
typedef struct
{
uint32_t a;
uint16_t b;
uint32_t c;
} foo;
这个结构包含在我从网络收到的一个大缓冲区中。
以下代码适用于 x86,但我在 ARM 上收到SIGBUS
。
extern void * buffer;
foo my_foo;
my_foo = (( foo * ) buffer)[0];
用 memcpy 替换指针取消引用解决了这个问题。
在ARM中搜索SIGBUS向我指出了一个事实,即这与内存对齐somwhow有关。
有人可以解释一下发生了什么吗?
你自己说过:你的特定处理器有内存对齐限制,buffer
没有对齐,允许从中读取大于一个字节。该分配可能被编译为较大实体的三个移动。
使用memcpy()
,没有对齐限制,它必须能够在任何两个地址之间复制,因此它可以做任何需要实现它的事情。可能逐字节复制直到地址对齐,这是一种常见的模式。
顺便说一句,我发现在没有数组索引的情况下编写代码更清晰:
extern const void *buffer;
const foo my_foo = *(const foo *) buffer;
C 标准 [ISO/IEC 9899:2011] - 6.3.2.3, 第 7 段:
指向对象或不完整类型的指针可以转换为指向其他对象或不完整类型的指针。如果生成的指针未正确对齐指向类型,则行为未定义。
来源:CERT 安全编码标准
ARM的系统期望结构在单词边界上对齐。如果不是这种情况,你可以有不同的行为(例如,在linux内核中,这些行为在/proc/cpu/alignement
中描述,其中之一是发送SIGBUS)。
您对memcpy()
所做的是强制数据结构对齐。
前,我在飞思卡尔IMX上开发了一些下载应用程序。那里有一个内存对齐问题(要求可执行文件是 512 字节的倍数)......手臂和x86之间的根本区别...但是使用 memcpy 要记住的是它逐字节复制......所以,它可能会起作用,但请务必检查运行时问题......不要被混蛋所迷惑...为您的特定平台设置内存对齐结构始终是一个好主意。