在嵌入式设备上投射指针

在32位嵌入式系统上投射和修改指针时遇到了一个奇怪的问题(运行contiki OS的 redbee econotag是特定的)。

uint32_t array[2]; array[0] = 0x76543210; array[1] = 0xfedcba98; uint8_t* point = ((uint8_t*)array)+1; printf("%08x \n", *(uint32_t*)point ); 

在我的电脑上输出:

 98765432 

嵌入式设备上的输出:

 10765432 

我的计算机的行为与我期望的一样,但嵌入式设备似乎在它到达单词的末尾时回滚。 为什么会这样?

您的目标“redbee econotag”被声明为具有ARMv4架构的ARM7。 ARMv4不提供像ARMv7或intel机器那样的未对齐内存访问。

引用ARM的文档 :

在ARMv4和ARMv5体系结构以及ARMv6体系结构(取决于其配置方式)上,在访问内存中的未对齐数据时需要小心,以免返回意外结果。 例如,当传统指针用于读取C或C ++源代码中的单词时,ARM编译器生成使用LDR指令读取单词的汇编语言代码。 当地址是四的倍数时,这可以正常工作,例如,如果它位于单词边界上。 但是, 如果地址不是4的倍数,则LDR返回旋转结果,而不是执行真正的未对齐字加载。 通常,这种旋转不是程序员所期望的

使用此代码,您将破坏严格别名规则:由point的对象由具有uint32_t类型的左值表达式访问。

C11(n1570),§6.5表达式
对象的存储值只能由具有以下类型之一的左值表达式访问:
– 与对象的有效类型兼容的类型,
– 与对象的有效类型兼容的类型的限定版本,
– 对应于对象的有效类型的有符号或无符号类型,
– 对应于对象有效类型的限定版本的有符号或无符号类型,
– 聚合或联合类型,其成员中包含上述类型之一(包括递归地,子聚合或包含联合的成员),或者
– 一个字符类型。

这会导致未定义的行为,因此任何事情都可能发生。

C11(n1570),§4。一致性
如果违反约束或运行时约束之外的“应该”或“不应该”的要求,则行为未定义。

 printf("%08x \n", *(uint32_t*)point ); 

此语句中的*表达式调用未定义的行为:它违反了别名规则,可能会执行未对齐的访问。

由于+1您执行32位值的未对齐访问,即地址不是4的倍数。

x86独立于对齐工作,因为它的根源一直回到8位机器(可能性能稍差)。

ARM需要对齐(与许多其他处理器一样),因此应将32位值放在四个字节的倍数的地址上。 如果不是这种情况,可能会发生各种不好的事情(错误的值,错误)。 对于数组,编译器会处理这个问题,但是当您显式地转换指针时,会强制它违反对齐。

编辑:请注意,这个答案的主体与它提示的评论无关

其他答案的理论很好,但可能对你没有帮助。 实际的问题是你写道:

 uint8_t* point = ((uint8_t*)array)+1; 

什么时候你应该写一些像

 uint8_t* point = (uint8_t*)(array+1); 

因为您需要将指针增加为指向适当类型的指针(以便增量操作将添加元素的大小),然后再将其转换为其他内容。

但有人可能会问你是否真的想要一个指向32位值的字节指针。 也许你打算以字节方式访问它(请注意字节顺序会因系统而异!)。 或者你真的打算指向一个32位值的指针,而这个指针又是指向其他地方的8位值的指针……