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]

但我希望将字节视为一长串位,如下所示:

[000000010000000100000001]

右移两位将导致这些位看起来像这样:

[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 所建议的,我将假设您希望将位从一个字节传送到下一个字节。

这里的要求是不够的。您需要指定相对于字节顺序的位顺序 - 当最低有效位超出一个字节时,会转到下一个更高或下一个较低字节。

但是,您所描述的内容过去在图形编程中非常普遍。您基本上已经描述了单色位图水平滚动算法。

假设“正确”意味着更高的地址但更低的有效位(即匹配两者的正常书写约定),单位移位将类似于......

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 上,这不是必需的,但速度要快得多。在某些处理器上,这是必要的。只需根据基 (address & 1)、(address & 3) 或 (address & 7) 进行切换,以在循环开始之前处理前几个字节。您还需要在主循环之后对尾随字节进行特殊处理。

于 2010-03-06T23:21:08.113 回答
1

这是我对两个字节的处理方式:

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 回答