在大端平臺上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!


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