在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位值的指针……
以上就是c/c++开发分享在嵌入式设备上投射指针相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/545781.html