0

私は C でビット操作を学んでおり、簡単なプログラムを作成しました。ただし、プログラムは失敗します。誰かがこのコードを調べてもらえますか? 基本的に、4バイトの「長い」変数を抽出して、個々のバイトに、またはその逆に再構築したいと考えています。これが私のコードです:

    printf("sizeof char= %d\n", sizeof(char));
    printf("sizeof unsigned char= %d\n", sizeof(unsigned char));
    printf("sizeof int= %d\n", sizeof(int));
    printf("sizeof long= %d\n", sizeof(long));
    printf("sizeof unsigned long long= %d\n", sizeof(unsigned long long));

    long val = 2;
    int k = 0;
    size_t len = sizeof(val);
    printf("val = %ld\n", val);
    printf("len = %d\n", len);

    char *ptr;
    ptr = (char *)malloc(sizeof(len));
    //converting 'val' to char array
    //val = b3b2b1b0 //where 'b is 1 byte. Since 'long' is made of 4 bytes, and char is 1 byte, extracting byte by byte of long into char
    //do{
        //val++;
    for(k = 0; k<len; k++){
        ptr[k] = ((val >> (k*len)) && 0xFF);
        printf("ptr[%d] = %02X\n", k,ptr[k]);
    }
    //}while(val < 12);

    //reassembling the bytes from char and converting them to long
    long xx = 0;
    int m = 0;
    for(m = 0; m< len; m++){
        xx = xx |(ptr[m]<<(m*8));
    }
    printf("xx= %ld\n", xx);

xx が 2 を返さないのはなぜですか?? また、「val」の値に関係なく、ptr[0] は 1 を格納しているようです :( 助けてください

前もって感謝します

4

3 に答える 3

4
ptr[k] = ((val >> (k*len)) && 0xFF);

する必要があります

ptr[k] = ((val >> (k*8)) & 0xFF);

&&条件ステートメントで使用され、& はビットごとの and に使用されます。また、値を文字に分割しているため、ループの各反復で、バイト内と同じ数のビットをシフトしたいと考えています。これはほとんどの場合 8 ですが、それ以外の場合もあります。ヘッダーファイルlimits.hには、それに関する情報があります。

于 2013-05-17T14:54:25.140 に答える
3

私が気づいたいくつかのこと:

  1. ビットごとの & の代わりにブール && 演算子を使用しています
  2. 「k*8」ではなく「k*len」でシフトしています
  3. 「len」だけでなく、「sizeof(len)」で配列を割り当てています
  4. 「unsigned char」の代わりに「char」を使用しています。これにより、「(ptr[m]<<(m*8))」式で負の数が返されることがあります。

したがって、コードの修正バージョンは次のようになります。

printf("sizeof char= %d\n", sizeof(char));
printf("sizeof unsigned char= %d\n", sizeof(unsigned char));
printf("sizeof int= %d\n", sizeof(int));
printf("sizeof long= %d\n", sizeof(long));
printf("sizeof unsigned long long= %d\n", sizeof(unsigned long long));

long val = 2;
int k = 0;
size_t len = sizeof(val);
printf("val = %ld\n", val);
printf("len = %d\n", len);

unsigned char *ptr;
ptr = (unsigned char *)malloc(len);
//converting 'val' to char array
//val = b3b2b1b0 //where 'b is 1 byte. Since 'long' is made of 4 bytes, and char is 1 byte, extracting byte by byte of long into char
//do{
    //val++;
for(k = 0; k<len; k++){
    ptr[k] = ((val >> (k*8)) & 0xFF);
    printf("ptr[%d] = %02X\n", k,ptr[k]);
}
//}while(val < 12);

//reassembling the bytes from char and converting them to long
long xx = 0;
int m = 0;
for(m = 0; m< len; m++){
    xx = xx |(ptr[m]<< m*8);
}
printf("xx= %ld\n", xx);

また、将来的には、このような質問はhttps://codereview.stackexchange.com/に適しています。

于 2013-05-17T15:15:59.533 に答える
1

他の人が今述べたように、ptr[k] = ((val >> (k*len)) && 0xFF);あなたがそれを望むかどうかはわかりません. &&演算子はブール演算子です。(value >> (k*len))がゼロ以外の値で、がゼロ以外の値である場合0xFF、格納される値ptr[k]は 1 になります。それがブール演算子の仕組みです。&の代わりに使用するつもりだったのかもしれません&&

さらに、型に適したシフト演算子を使用することを選択しましたunsignedが、符号付き型にはさまざまな移植性のない側面があります。xx = xx |(ptr[m]<<(m*8));たとえば、符号付き整数のオーバーフローが発生する可能性があるため、未定義の動作を引き起こす可能性があります。

C では、sizeof (char)常に1です。これは、型を表すために使用される s の数がsizeof演算子によって示されるためです。char例えば。s を表すために使用される s の数をsizeof (int)示します。変わるものです。したがって、コードはa 型に依存するべきではありません。charintCHAR_BITsizeof

int実際、コードを移植可能にしたい場合は、たとえばに 32767 より大きい値または -32767 より小さい値を格納できると期待すべきではありません。パディング ビットが存在する可能性があるため、これはサイズに関係ありません。要約すると、sizeofa 型は、格納できる値のセットを必ずしも反映しているわけではありません!


アプリケーションの変数のタイプを選択します。移植可能です。アプリケーションがその範囲を超える値を必要としない場合は、問題ありませんint。それ以外の場合はlong int、移植可能に -2147483647 と 2147483647 の間 (および含む) の値を格納できる の使用を検討することをお勧めします。それを超える値が必要な場合は、 a を使用しますlong long int。これにより、少なくとも -9223372036854775807 から 9223372036854775807 までの値で構成される保証範囲が得られます。それを超える値は、おそらくGMPなどの多精度演算ライブラリに値します。

負の値を使用する予定がない場合は、unsigned型を使用する必要があります。

移植可能な整数型の選択を考慮すると、これらの整数をファイルに書き込み、ファイルからそれらの整数を読み取る移植可能な方法を考案できることが理にかなっています。符号と絶対値を次のように抽出しますunsigned int

unsigned int sign = val < 0; /* conventionally 1 for negative, 0 for positive */
unsigned int abs_val = val;
if (val < 0) { abs_val = -abs_val; }

...そして、 と の 8 ビット チャンクの配列を作成し、abs_val一緒signにマージします。int-32767 から 32767 までの値しか格納できないため、16 ビットしか格納できないというポータブルな意思決定を使用することを既に決定しています。その結果、ループやビットごとのシフトは必要ありません。乗算を使用して符号ビットを移動し、除算/モジュロを使用して絶対値を減らすことができます。符号は従来、配列の先頭 (ビッグ エンディアン) または末尾 (リトル エンディアン) にある最上位ビットに付いていると考えてください。

unsigned char big_endian[] = { sign * 0x80 + abs_val / 0x100,
                               abs_value % 0x100 };
unsigned char lil_endian[] = { abs_value % 0x100,
                               sign * 0x80 + abs_val / 0x100 };

このプロセスを逆にするために、反対の演算を互いに逆に実行します (つまり、乗算の代わりに除算とモジュロを使用し、除算と加算の代わりに乗算を使用し、符号ビットを抽出して値を再形成します)。

unsigned int big_endian_sign = array[0] / 0x80;
int big_endian_val = big_endian_sign
                   ? -((array[0] % 0x80) * 0x100 + array[1])
                   :  ((array[0] % 0x80) * 0x100 + array[1]);

unsigned int lil_endian_sign = array[1] / 0x80;
int lil_endian_val = lil_endian_sign
                   ? -((array[1] % 0x80) * 0x100 + array[0])
                   :  ((array[1] % 0x80) * 0x100 + array[0]);

のコードはもう少し複雑にlongなり、2 項演算子を使用する価値があります。符号と絶対値の抽出は基本的に同じままで、変更点は変数の型だけです。移植可能に表現可能な値のみを気にするという決定を下したため、ループはまだ必要ありません。long valaから an に変換する方法は次のunsigned char[4]とおりです。

unsigned long sign = val < 0; /* conventionally 1 for negative, 0 for positive */
unsigned long abs_val = val;
if (val < 0) { abs_val = -abs_val; }

unsigned char big_endian[] = { (sign << 7) | ((abs_val >> 24) & 0xFF),
                               (abs_val >> 16) & 0xFF,
                               (abs_val >> 8) & 0xFF,
                               abs_val & 0xFF };
unsigned char lil_endian[] = { abs_val & 0xFF,
                               (abs_val >> 8) & 0xFF,
                               (abs_val >> 16) & 0xFF,
                               (sign << 7) | ((abs_val >> 24) & 0xFF) };

...そして、署名された値に戻す方法は次のとおりです。

unsigned int big_endian_sign = array[0] >> 7;
long big_endian_val = big_endian_sign
                   ? -((array[0] & 0x7F) << 24) + (array[1] << 16) + (array[2] << 8) + array[3]
                   :  ((array[0] & 0x7F) << 24) + (array[1] << 16) + (array[2] << 8) + array[3];

unsigned int lil_endian_sign = array[3] >> 7;
long lil_endian_val = lil_endian_sign
                   ? -((array[3] & 0x7F) << 24) + (array[2] << 16) + (array[1] << 8) + array[0]
                   :  ((array[3] & 0x7F) << 24) + (array[2] << 16) + (array[1] << 8) + array[0];

unsignedとタイプのスキームを考案するのはあなたに任せますlong long...そしてコメントのためにフロアを開きます:

于 2013-05-17T15:58:12.773 に答える