0

Web上でgccbyteswapやその他の代替手段を使用してこの質問に対する回答があることは知っていますが、以下のコードが機能しない理由を疑問に思っていました。

まず、gcc警告(来るべきではないと感じます)があり、byteswapを使用したくない理由は、マシンがビッグエンディアンかリトルエンディアンかを判断し、それに応じてbyteswapを使用する必要があるためです。私のマシンはビッグエンディアンです。変換せずにバイトをそのまま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]

私はこれをリトルエンディアンのマシンで実行しているので、byteswapとntohl_64はまったく同じ値になるはずですが、残念ながら、まったく予期しない結果が得られます。誰かが何が悪いのかを指摘できれば素晴らしいと思います。

4

4 に答える 4

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]はint であるためunsigned char、左シフトを , として実行し、結果を返します。これらすべてのシフトの型を unsigned long long にしたいので、これはあなたが望むものではありません。56pp[0] << 56unsigned charunsigned char

これを修正する方法は、 のようにキャストすることです((unsigned long long)pp[0]) << 56

于 2012-10-01T15:34:44.110 に答える
2

コードが機能しない理由は、unsigned chars をシフトしているためです。それらがシフトすると、ビットが上から落ち、7 を超えるシフトは 0 を返すと見なすことができます (ただし、一部の実装では、マシン コードのシフトの動作方法が原因で奇妙な結果になりますが、x86 はその例です)。最終的なサイズを最初のようにしたいものにキャストする必要があります。

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

gcc での最適な解決策は、htobe64 を使用することです。この関数はあなたのためにすべてを行います。

PS少し話題から外れていますが、関数をエンディアン間で移植可能にしたい場合は、次のようにします。

Nova Denizen のコメントに基づいて編集します。

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 バイト値として扱うと仮定すると、そのコードはどのシステムでも機能するはずです。運が良ければ、コンパイラは、ビッグ エンディアン システムを使用している場合は式全体を最適化し、リトル エンディアン マシンを使用している場合は組み込みのバイト スワッピング手法を使用できます (他のシステムでも動作することが保証されています)。機械の種類)。

于 2012-10-01T15:22:05.410 に答える
1

pp[x]は 8 ビット幅であるため、式の結果pp[0] << 56はゼロになります。元の値を明示的にマスキングしてからシフトする必要があります。

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

いずれにせよ、コンパイラの組み込みを使用するだけで、通常は単一のバイトスワップ命令になります。

于 2012-10-01T15:15:03.860 に答える
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]);
}
于 2012-10-01T15:36:55.220 に答える