1

バイト配列を long に変換しようとしました

long readAndSkipLong(char*& b)
{
    unsigned long ret = (b[0] << 56) | (b[1] << 48) | (b[2] << 40) | (b[3]<<32) | (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]);
    return ret;
}

私のシフトが正しくないようです。意図した値に対して

152  --> 00000000 00000000 00000000 00000000 00000000 00000000 00000000 10011000

私は得る:

-104  --> 11111111 11111111 11111111 11111111 11111111 11111111 11111111 10011000 

バグがどこにあるのか分かりますか?

4

3 に答える 3

3

型の昇格と符号拡張のためです。配列内のすべての値charは符号付きで、ビットシフトは整数演算です。シフト演算子を使用すると、 に評価され、が署名されているintため、それらをシフトすると符号付き が生成されます。charint

最後の (右端の) バイトは1符号ビットです。に昇格するとint、その値は-104符号拡張されます。残りの数値の OR をとったので、すべての1ビットは影響を受けませんでした。

この問題を回避するには、シフトおよび OR する前に各chars をキャストできます。unsigned long

他にできることは、 likeでそれぞれcharをビットごとに AND 演算することです。と AND 演算するとが生成され、最下位 8 ビットはそのままで、左にゼロがあり、符号拡張はありません。0xff((b[i] & 0xff) << 24)0xffint

于 2015-03-20T17:19:53.473 に答える
-1

考慮すべきいくつかのこと

  1. 符号に問題がないように、and for you タイプを含めcstdintて使用します。std::uint64_tstd::uint8_t
  2. ロジックは、マシンがリトル エンディアンかビッグ エンディアンかによっても異なります。リトル エンディアン マシンの場合、最下位バイトを最初に配置し、次に上位に配置する必要があります。あなたのロジックはビッグエンディアン用です。
  3. カウントオーバーフローが発生している可能性があります。より良い方法は、 a を明示的に宣言してuint64_tそれを使用することです。

uint64_tこれは、リトルエンディアンマシンでバイト用に書いたコードの一部です。

std::uint64_t bytesToUint64(std::uint8_t* b) {
    std::uint64_t msb = 0x0u;
    for (int i(0); i < 7; i++) {
        msb |= b[i];
        msb <<= 8;
    }
    msb |= b[7];

    return msb;
}

OPによる編集(実装されたヒント1):

long readAndSkipLong(char*& b)
{
    std::uint64_t ret = 
        ((std::uint8_t)b[0] << 56) | 
        ((std::uint8_t)b[1] << 48) | 
        ((std::uint8_t)b[2] << 40) | 
        ((std::uint8_t)b[3] << 32) | 
        ((std::uint8_t)b[4] << 24) | 
        ((std::uint8_t)b[5] << 16) | 
        ((std::uint8_t)b[6] <<  8) | 
        ((std::uint8_t)b[7]);
    b+=8;

    return ret;
}
于 2015-03-20T19:26:49.243 に答える