问题
先看一段代码
//说明:uint32_t数据类型为4byte无符号整数,其他类型依此类推
uint32_t a = 0;
uint16_t b = 0x1223;
uint8_t *ptr = NULL;
ptr = (uint8_t *)&b;
memcpy((uint8_t *)&a, ptr, sizeof(b));
printf("a = 0x%x\r\n",a);
这样写有没有问题?
这段代码运行结果是什么?
是 a = 0x1223 不?
在linux平台、一些WIFI模组+freeRTOS平台运行结果都是 0x1223,
在keil_C51单片机平台运行结果是 0x12230000。
进一步增加代码,加log打印。
uint32_t a = 0;
uint16_t b = 0x1223;
uint8_t *ptr = NULL;
uint32_t x = 0x12345678; /*305414896 */
uint8_t *xp = (uint8_t *)&x;
printf("x = %d\r\n",x);
pritnf("xp[0]:%x\r\n xp[1]:%x\r\n xp[2]:%x\r\n xp[3]:%x\r\n", xp[0],xp[1],xp[2],xp[3]);
printf("sizefo short =%d sizeof int =%d\r\n",sizefo(uint16_t), sizeof(uint32_t));
ptr = (uint8_t *)&b;
memcpy((uint8_t *)&a, ptr, sizeof(b));
printf("short b= 0x%x int a = 0x%x\r\n",b,a);
keil_C51运行结果:
linux 64位 运行结果:
从打印log看到,在keil_C51环境中,
数据0x12345678,从地址地位到高位的排列顺序是:12、34、56、78,
低地址存着高字节数据,是大端存储模式,如下:
低 | 12 |
34 | |
56 | |
高 | 78 |
而linux环境中数据从低位到高位的排列顺序是:78、56、34、12,
低地址存着低字节数据,是小端存储模式,如下:
低 | 78 |
56 | |
34 | |
高 | 12 |
而memcpy函数是不管你大端小端,他只负责把源地址的数据复制到目的地址,你传进来参数告诉我复制多少字节,就复制多少字节。
ptr 以及 (uint8_t*)&a中分别是b 和a的 起始地址,把b中数据从低地址开始copy给a 的地址。
在keil_C51中,b中数据内存中存储排列为:
低 | 12 |
高 | 23 |
拷贝后,a中存储排列为:
低 | 12 |
高 | 23 |
00 | |
00 |
在keilC51中,编译器用大端模式来处理上面的数据,那就等同于12是数据的最高字节,因此这个数是 0x12230000.
在linux环境中,b中数据存储排列为:
低 | 23 |
高 | 12 |
拷贝后,a 的存储排列为:
低 | 23 |
12 | |
00 | |
高 | 00 |
按小端模式来处理上面数据,则23是一个数的 最低字节,因此这个数是0x00001223.
没想到吧?51单片机居然是大端模式。上面那样的代码相信我们都写过,从来没出错,是因为我们用的平台大多是小端模式,很少遇到大端模式的。
总结
小端模式数据可以取地址把数据拷贝到另一个数的地址,数值不会变,
大端模式不能这样随意拷贝,拷贝后需要进行高低字节交换。
延伸
上文说的是keil_C51,51单片机是8位的,无所谓大端小端,由编译的工具链进行处理,
查到一份文档说明,在keil中,多字节数据被当成大端处理,在IAR8051中,多字节的数则是被当成小端来处理的。
如下图,
4.【喂到嘴边了的模块】超级嵌入式系统“性能/时间”工具箱~
免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。