C / C ++:动态分配内存的按位运算符

时间:2010-03-06 23:02:19

标签: c++ c bitwise-operators

在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]

右移2会导致比特看起来像这样:

[000000000100000001000000]

当分成3个字节(因此是0,64,64)时,最终看起来像这样:

[00000000][01000000][01000000]

任何想法?我是否应该制作一个结构/类并重载相应的运算符?编辑:如果是的话,有关如何进行的任何提示?注意:我正在寻找一种方法来实现这一点(有一些指导)作为学习经验。

5 个答案:

答案 0 :(得分:2)

  • 从accessor / mutators中分离出来的分配
  • 接下来,查看像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 ];
}

答案 1 :(得分: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&lt;&lt;&lt; 7)是否为非零。如果你的字节在std :: vector中,那么在任何一端插入应该没问题。

编辑我应该说 - 优化一次处理2个,4个或8个字节会产生对齐问题。例如,当从未对齐的字符串数组中读取2字节字时,最好首先读取奇数字节,以便后面的字读取都在偶数地址处,直到循环结束。

在x86上,这不是必需的,但 要快得多。在某些处理器上,这是必要的。只需根据基数(地址&amp; 1),(地址&amp; 3)或(地址&amp; 7)进行切换,以便在循环之前处理开始时的前几个字节。您还需要特殊情况下主循环后的尾随字节。

答案 2 :(得分:1)

以下是我将如何处理两个字节:

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

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

从那里,您可以将其概括为n个字节的循环。为了灵活性,您需要生成幻数(0x3和6)而不是硬编码。

答案 3 :(得分: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;
}

玩它吧。你会明白我的想法。

答案 4 :(得分:0)

运算符重载是语法糖。它实际上只是一种调用函数并传递字节数组的方法,而不是像调用函数一样

所以我首先要编写这个函数

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

然后,如果你想将它包装在一个运算符重载中以便更容易使用,或者因为你只是喜欢这种语法,你也可以这样做。或者你可以调用该函数。