指针强制转换and字节序导致的问题
指针强制转换and字节序导致的问题
平时接触的小端设备居多,昨天在一个大端设备上遇到了一个问题,调试分析后发现是字节序问题。
现象:
类型 | 期望值(dec) | 实际值(dec) | 期望值(bin) | 实际值(bin) |
---|---|---|---|---|
uint16_t | 1 | 256 | 0000 0000 0000 0001 | 0000 0001 0000 0000 |
uint16_t | 2 | 512 | 0000 0000 0000 0010 | 0000 0010 0000 0000 |
uint16_t | 3 | 768 | 0000 0000 0000 0011 | 0000 0011 0000 0000 |
uint16_t | 13 | 3328 | 0000 0000 0000 1101 | 0000 1101 0000 0000 |
uint16_t | 14 | 3584 | 0000 0000 0000 1110 | 0000 1110 0000 0000 |
只看十进制的话没有经验的话还不容易看出是字节序的问题,当分析下二级制后就很容易有所发现了。
接下来看下代码:
uint16_t pid{0};
uint32_t ret = FUNC(reinterpret_cast<uint8_t*>&pid); // 原型uint32_t FUNC(uint8_t *);
期望调用FUNC后获取到表格上期望的值,但是实际值由于大小端的字节序原因出现了预期之外的值。
原因的话不在重复造轮子了,找到一篇讲解很细致的博客,引用下,感谢博主。
[原文]: https://blog.csdn.net/javajiawei/article/details/73277699
在C语言开发的过程中,发现不同硬件平台出现了一定差异性,例如在x86_64运行正常,二在xlp运行失败,分析如下。
x86_64是小端序,而xlp是大端序。
排查代码的时候,发现在相应申请内存的地方出现了强制转换。
简化原来的代码如下:
DWORD FUNC_A(ULONG *pulMemSize)
{
dwRet = FUNC_B ( (DWORD *)pulMemSize);
return 0;
}
其中DWORD为宏定义的unsigned int,而ULONG为unsigned long。由于FUNC_B为一个静态库提供的接口,同时入参为DWORD类型的指针。因此在这个地方进行了强制的转换。因为从ULOGN指针转换为DWORD指针,因此转换之后的DWORD指针的范围为pulMemSize这个指针所指向内存地址的前四个字节。
来看一下变量内容是如何存储的。
在小端序的系统(x86_64)中,如果*pulMemSize = ABCDEFGH,那么内存的存储顺序为HGFEDCBA(低地址存储变量低位),因此在进行指针转换的时候DWORD指针取值为HGFE,也就是说在 FUNC_B中对pulMemSize所指变量的操作是基于HGFE所在的内存空间,而由于HGFE所在的内存空间在小端序的系统中所表示的恰恰也是pulMemSize所指向变量的低位部分,因此这样操作是没有问题的(前提是取值没有超过32位大小)。
但是在大端序的系统中,pulMemSize所指向变量的存储顺序为ABCDEFGH,而FUNC_B对pulMemSize所指变量的操作则是基于ABCD所在的内存空间,但是ABCD所表示的是pulMemSize所指向变量的高位部分。因此最后在以ULONG来取pulMemSize表示的内存值的时候,则会出错。
更新之后的代码为:
DWORD FUNC_A(ULONG *pulMemSize)
{
DWORD dwMemSize = 0;
dwRet = FUNC_B ( &dwMemSize);
*pulMemSize = (ULONG)dwMemSize;
return 0;
}
可以看出这个dwMemSize这个中间临时变量作用大大的,因此以后如果见到这种看似没有的中间变量,不要轻易删除。
其实这个问题说明的一个问题是,正常变量的强制转换。不同的硬件平台会自动的补齐或者截断对应的高位数据,但是对于指针的操作,由于指针总是从变量起始的位置开始寻址,因此要千万注意指针的强制转换。在不同的硬件系统可能会产生差异性。
Tips:
要格外注意大端模式中对内存低地址的修改!!
- 大端模式中,低地址存放的是变量的高位,
*pulMemSize = ABCDEFGH
,那么内存中存储为 ABCDEFGH,修改内存低地址,将会影响到变量的高位; - 小端模式中,地地址存放的是变量低位,
*pulMemSize = ABCDEFGH
,那么内存中存储为 HGFEDCBA;
(gdb) p/x *pdata
$2 = 0x4030201
(gdb) x/4xb pdata
0x7fffffffdff0: 0x01 0x02 0x03 0x04
大端模式,内存中顺序就是变量值的顺序,便于看,网络序为大端说不定就是为了好看:)
小端模式,虽然打印内存和变量值是逆序的,但是低地址存低位,没有什么弯子,对于指针强转这些不会有问题,更踏实_