2012-10-01 56 views
0

我知道有這個問題的答案使用gcc byteswap和其他替代品在網絡上,但想知道爲什麼我的代碼下面不工作。c + + 64位網絡到主機翻譯

首先我有GCC警告(我覺得不應該推出)和原因,我不希望使用字節交換是因爲我要確定我的機器是big endian還是little endian和使用字節交換因此我。如果我的機器是big endian我可以memcpy字節原樣沒有任何翻譯,否則我需要交換和複製它。

static inline uint64_t ntohl_64(uint64_t val) 
{ 
    unsigned char *pp =(unsigned char *)&val; 
    uint64_t val2 = (pp[0] << 56 | pp[1] << 48 
        | pp[2] << 40 | pp[3] << 32 
        | pp[4] << 24 | pp[5] << 16 
        | pp[6] << 8 | pp[7]); 
    return val2; 
} 

int main() 

{ 
    int64_t a=0xFFFF0000; 
    int64_t b=__const__byteswap64(a); 
    int64_t c=ntohl_64(a); 
    printf("\n %lld[%x] [%lld] [%lld]\n ", a, a, b, c); 
} 

Warnings:- 
In function \u2018uint64_t ntohl_64(uint64_t)\u2019: 
    warning: left shift count >= width of type 
    warning: left shift count >= width of type 
    warning: left shift count >= width of type 
    warning: left shift count >= width of type 


Output:- 
4294901760[00000000ffff0000] 281470681743360[0000ffff00000000] 65535[000000000000ffff] 

我有點endian機器上運行此所以字節交換和ntohl_64應該產生完全相同的值,但不幸的是我得到完全意想不到的結果。如果有人能指出什麼是錯的,那將會很棒。

+3

您是否考慮過解決警告? – PlasmaHH

+0

嗨,我不確定爲什麼首先有警告,因爲我的班次恰好適合64位整數,並且不應該有損失 – Shanky

+1

您是否檢查過您正在轉換的是什麼類型? – PlasmaHH

回答

1

由於pp[x]爲8位寬,因此表達式pp[0] << 56的結果爲零。你需要對原值明確的遮蔽,然後轉移:

uint64_t val2 = ((val & 0xff) << 56) | 
       ((val & 0xff00) << 48) | 
       ... 

在任何情況下,只要使用編譯器內置插件,它們通常會導致一個單字節交換指令。

+0

好吧,我不必經常換。如果機器是big endian,我可以使用原來的值和上面的函數,如果工程會得到相同的結果。我認爲,如果我使用你建議的方式,我總是會交換。 – Shanky

+0

不要在運行時這樣做,這是浪費。找出你正在構建的平臺,並使用宏/模板/任何。 BE和LE之間很少有體系結構可以翻轉。 –

+2

是的,問題是移位運算符的右側操作數大於它們操作的類型的寬度,因爲編譯器用「移位計數> =類型寬度」警告指示。這導致未定義的行爲(不一定爲零)。另外重要的是要知道對操作數執行整型提升爲'<<',所以'char a = 1;一個<< 24;'可以正常工作,但是一個<< 48;'不會(假設整數寬度爲32位)。 – bames53

2

您的代碼不起作用的原因是因爲您正在轉移unsigned char s。當它們移位時,位數從最高位開始下降,任何大於7的位移都可以返回0(儘管由於機器碼轉換工作的方式,某些實現最終會產生奇怪的結果,x86就是一個例子)。你必須將它們轉換成任何你想要的最終大小爲第一,如:

((uint64_t)pp[0]) << 56 

你用gcc最佳的解決方案是使用htobe64。這個功能爲你做了一切。

P.S.這是題外話了一點點,但如果你想跨越字節序的功能的便攜式你可以這樣做:

編輯基於新星外來語的評論:

static inline uint64_t htonl_64(uint64_t val) 
{ 
    union{ 
     uint64_t retVal; 
     uint8_t bytes[8]; 
    }; 

    bytes[0] = (val & 0x00000000000000ff); 
    bytes[1] = (val & 0x000000000000ff00) >> 8; 
    bytes[2] = (val & 0x0000000000ff0000) >> 16; 
    bytes[3] = (val & 0x00000000ff000000) >> 24; 
    bytes[4] = (val & 0x000000ff00000000) >> 32; 
    bytes[5] = (val & 0x0000ff0000000000) >> 40; 
    bytes[6] = (val & 0x00ff000000000000) >> 48; 
    bytes[7] = (val & 0xff00000000000000) >> 56; 

    return retVal; 
} 

static inline uint64_t ntohl_64(uint64_t val) 
{ 
    union{ 
     uint64_t inVal; 
     uint8_t bytes[8]; 
    }; 

    inVal = val; 

    return bytes[0] | 
     ((uint64_t)bytes[1]) << 8 | 
     ((uint64_t)bytes[2]) << 16 | 
     ((uint64_t)bytes[3]) << 24 | 
     ((uint64_t)bytes[4]) << 32 | 
     ((uint64_t)bytes[5]) << 40 | 
     ((uint64_t)bytes[6]) << 48 | 
     ((uint64_t)bytes[7]) << 56; 
} 

假設編譯器不做某事到uint64_t,通過返回,並假設用戶將結果視爲一個8字節的值(而不是整數),該代碼應該在任何系統上工作。如果運氣好的話,如果你在一個大的endian系統上,你的編譯器將能夠優化整個表達式,並且如果你在一個小型的endian機器上使用一些內置的字節交換技術(並且它保證仍然可以在其他任何其他的機器上工作)一種機器)。

+0

該代碼取決於所有系統都是普通香草big-endian或little-endian的假設,如果可移植性是一個問題,這不是一個好的假設。請參閱Rob Pike的[Byte Order Fallacy](http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html)。 – NovaDenizen

+0

@NovaDenizen我在該博客文章中找不到任何內容,表明存在非大/小端機器。雖然你認爲處理假想機器的情況是正確的,但你所提供的任何東西都暗示着這樣的機器存在。 – CrazyCasta

+0

同意。這就是編程與標準之間的區別,只是說「它適用於這些機器」。 – NovaDenizen

2
uint64_t val2 = (pp[0] << 56 | pp[1] << 48 
       | pp[2] << 40 | pp[3] << 32 
       | pp[4] << 24 | pp[5] << 16 
       | pp[6] << 8 | pp[7]); 

pp[0]unsigned char56是int,所以pp[0] << 56執行左移作爲unsigned char,與unsigned char結果。這不是你想要的,因爲你想要所有這些班次都有無符號長整型。

解決此問題的方法是投射,如((unsigned long long)pp[0]) << 56

+0

根據移位值的類型,移位的值不會被提升。作爲一個例子,下面的代碼用gcc輸出值1。 uint8_t x = 1; uint64_t c32 = 32,y; y = x << c32; printf(「%llu」,y); – CrazyCasta

0

鑄造和轉換工作PlasmaHH暗示,但我不知道爲什麼32位上移自動上變換而不是64位。

typedef uint64_t __u64; 

static inline uint64_t ntohl_64(uint64_t val) 
{ 
unsigned char *pp =(unsigned char *)&val; 
return ((__u64)pp[0] << 56 | 
     (__u64)pp[1] << 48 | 
     (__u64)pp[2] << 40 | 
     (__u64)pp[3] << 32 | 
     (__u64)pp[4] << 24 | 
     (__u64)pp[5] << 16 | 
     (__u64)pp[6] << 8 | 
     (__u64)pp[7]); 
} 
+0

數字文字的類型爲int,二元運算符將不同參數類型的「低位」提升爲「高位」。所以每個pp [0]都被提升爲int。 – NovaDenizen

+0

@NovaDenizen這兩個操作數都轉換爲這兩種類型中的較大者是正確的,但整數提升在將int小於int的類型轉換爲int之前有一個步驟。所以'char(5)<< char(15)'具有明確定義的行爲,即使雙方都只是char而15是大於char的寬度。 – bames53