4

C / C ++では、動的に割り当てられたメモリにビット演算子(特に左/右シフト)を適用する簡単な方法はありますか?

たとえば、私がこれを行ったとしましょう:

unsigned char * bytes=new unsigned char[3];
bytes[0]=1;
bytes[1]=1;
bytes[2]=1;

これを行う方法が欲しいのですが:

bytes>>=2;

(この場合、「バイト」の値は次のようになります):

bytes[0]==0
bytes[1]==64
bytes[2]==64

値をそのようにする必要がある理由:

割り当て後、バイトは次のようになります。

[00000001][00000001][00000001]

しかし、私はバイトを次のように1つの長いビット文字列として扱いたいと考えています。

[000000010000000100000001]

2つ右にシフトすると、ビットは次のようになります。

[000000000100000001000000]

これは、3バイト(つまり、0、64、64)に分割すると、最終的に次のようになります。

[00000000][01000000][01000000]

何か案は?構造体/クラスを作成して適切な演算子をオーバーロードする必要がありますか?編集:もしそうなら、どのように進めるかについてのヒントはありますか?注:私はこれを(ある程度のガイダンスとともに)学習体験として自分で実装する方法を探しています。

4

5 に答える 5

2
  • アクセサ/ミューテータから割り当てを切り離します
  • 次に、のような標準的なコンテナbitsetがあなたのために仕事をすることができるかどうかを確認してください
  • それ以外の場合はチェックアウトboost::dynamic_bitset
  • すべてが失敗した場合は、自分のクラスをロールします

大まかな例:

typedef unsigned char byte;

byte extract(byte value, int startbit, int bitcount)
{
   byte result;
   result = (byte)(value << (startbit - 1));
   result = (byte)(result >> (CHAR_BITS - bitcount));
   return result;
}

byte *right_shift(byte *bytes, size_t nbytes, size_t n) {
   byte rollover = 0;
   for (int i = 0; i < nbytes; ++i) {
     bytes[ i ] = (bytes[ i ] >> n) | (rollover < n);
     byte rollover = extract(bytes[ i ], 0, n);
   }
   return &bytes[ 0 ];
}
于 2010-03-06T23:06:46.530 に答える
2

John Knoellerが示唆しているように、あるバイトから次のバイトにビットを運ぶ必要があると仮定します。

ここでの要件は不十分です。バイトの順序に対するビットの順序を指定する必要があります。最下位ビットが1バイトから外れると、次に上位または次の下位バイトに移動します。

ただし、あなたが説明していることは、グラフィックプログラミングでは非常に一般的でした。基本的に、モノクロビットマップの水平スクロールアルゴリズムについて説明しました。

「右」がより高いアドレスを意味するが、重要度の低いビットを意味すると仮定すると(つまり、両方の通常の書き込み規則に一致する)、シングルビットシフトは次のようになります...

void scroll_right (unsigned char* p_Array, int p_Size)
{
  unsigned char orig_l = 0;
  unsigned char orig_r;

  unsigned char* dest = p_Array;

  while (p_Size > 0)
  {
    p_Size--;

    orig_r  = *p_Array++;
    *dest++ = (orig_l << 7) + (orig_r >> 1);

    orig_l = orig_r;
  }
}

可変シフトサイズにコードを適合させることは大きな問題ではないはずです。最適化の明らかな機会があります(たとえば、一度に2、4、または8バイトを実行する)が、それはあなたに任せます。

ただし、左にシフトするには、別のループを使用する必要があります。このループは、最上位のアドレスから始まり、下に向かって機能する必要があります。

「オンデマンド」で拡張する場合は、orig_l変数に上記の最後のバイトが含まれていることに注意してください。オーバーフローをチェックするには、(orig_l << 7)がゼロ以外であるかどうかをチェックします。バイトがstd::vectorにある場合、どちらかの端に挿入しても問題はありません。

編集 私が言ったはずです-一度に2、4、または8バイトを処理するように最適化すると、アライメントの問題が発生します。たとえば、整列されていないchar配列から2バイトのワードを読み取る場合は、最初に奇数バイトの読み取りを実行して、ループの最後まで後のワード読み取りがすべて偶数アドレスになるようにするのが最適です。

x86ではこれは必要ありません、はるかに高速です。一部のプロセッサでは、それが必要です。ベース(アドレス&1)、(アドレス&3)、または(アドレス&7)に基づいて切り替えを行うだけで、ループの前の最初の数バイトを処理できます。また、メインループの後の末尾のバイトを特殊なケースにする必要があります。

于 2010-03-06T23:21:08.113 に答える
1

これが2バイトでそれを行う方法です:

unsigned int rollover = byte[0] & 0x3;
byte[0] >>= 2;

byte[1] = byte[1] >> 2 | (rollover << 6);

そこから、これをnバイトのループに一般化できます。柔軟性を高めるために、マジックナンバー(0x3および6)をハードコーディングするのではなく、生成することをお勧めします。

于 2010-03-06T23:18:42.160 に答える
1

私はこれに似たものを調べます:

#define number_of_bytes 3

template<size_t num_bytes>
union MyUnion
{
    char            bytes[num_bytes];
    __int64         ints[num_bytes / sizeof(__int64) + 1];
};

void main()
{
    MyUnion<number_of_bytes> mu;
    mu.bytes[0] = 1;
    mu.bytes[1] = 1;
    mu.bytes[2] = 1;
    mu.ints[0] >>= 2;
}

遊んでみてください。あなたは私が信じている考えを得るでしょう。

于 2010-03-07T00:18:28.427 に答える
0

演算子のオーバーロードは構文糖衣です。これは、実際には、関数を呼び出しているように見せることなく、関数を呼び出してバイト配列を渡す方法にすぎません。

だから私はこの関数を書くことから始めます

 unsigned char * ShiftBytes(unsigned char * bytes, size_t count_of_bytes, int shift);

次に、使いやすくするために、または単にその構文を好むために、これを演算子オーバーロードでラップしたい場合は、それを行うこともできます。または、関数を呼び出すこともできます。

于 2010-03-06T23:18:39.787 に答える