在大端平台上scanf输入半字产生的内存覆盖问题调查

上周写了一个socket客户端程序,定义

uint16_t	code;//				两个字节十六进制的code码
scanf("%x",&code);

编译正常,无警告,在intel平台程序正常工作,但在powerpc上程序错误,排查后,猜测就在这两句程序出错了。

写了一个测试程序

#include <stdio.h>
#include <stdint.h>
    uint16_t i;
int main()
{
    scanf("%x",&i);
    printf("%x\n",i);
}

在本地编译运行,输入0x01,输出1,正常

在飞思卡尔powerpc p2020运行,输入0x01,输出0,错误

同样的程序,运行在不同平台产生的结果不同,星哥立马就想到了大小端问题,powerpc是大端模式,高地址存放高位,intel则是小端。

好了,问题点定位出来了,应该怎么解决呢?

首先我们要明确,这不可能是编译器的问题,在pc上是使用gcc编译,在powerpc上使用的是交叉编译链,程序肯定是可以适应不同平台的!


在咨询很多网友后,认为问题很可能是出在scanf("%x",&code)上,%x这个格式存储需要四字节。而且这个小demon很容易发生段错误,使用gdb调试,发现时在程序结束,return语句写入错误,经分析,是我声明的2字节的code,写入了四个字节,覆盖掉了后边两个字节,属于run time error。

在powerpc上再次运行demon,输入0x00010000,神奇的打印出了1!

经过分析,在powerpc上输入0x00010000时的内存输入如下:

地址 数据

0x00 00

0x01 01

0x02 00

0x03 00

输入0x0时的内存地址数据如下

powerpc x86

地址 数据 地址 数据

0x00 00 0x00 00

0x01 00 0x01 00

0x02 00 0x02 00

0x03 01 0x03 01

由于uint16_t只申请了两个字节的内存,即0x00和0x01地址,因此读取到了0值。而在x86平台读取的是0x02和0x03地址中的值,即01,即使覆盖了其他内存,x86上仍然可以得到预计结果!但这种内存覆盖错误很容易产生不可思议的bug!


解决方法:

#include <stdio.h>
#include <stdint.h>
    uint16_t i;
int main()
{
    scanf("%hx",&i);
    printf("%x\n",i);
}

申请了正确的内存空间,很容易就解决了。还有一个疑点,就是类型的隐式提升,这个还需要继续调查思考,以后另开一篇。


总结:

如果你的代码没有针对特定平台的开发和操作,而在不同平台有不同的结果,那么别思考编译器设置或者添加对特定平台的适应性操作,肯定是你代码有bug!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章