2010-03-06 101 views
3

在C/C++中,是否有一种简单的方法将位运算符(特别是左/右移)应用于动态分配内存?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] 

任何想法?我应该做一个结构/类并重载适当的操作符?编辑:如果是这样,任何提示如何进行?注意:我正在寻找一种方法来自己实施(有一些指导)作为学习体验。

+0

你会期望这个每个单独移动?或者将一个字节的位传送到下一个字节? – 2010-03-06 23:03:50

+0

你的意思是你想要转移跨越你访问的字节的边界吗?我假设你也希望能够做到这一点,比如说4个字节,这对于整型和轮班来说可能是安全的。 – 2010-03-06 23:04:08

+0

我编辑了我的帖子,以显示转换后的值。对不起,原来缺乏清晰度。 (@John,我希望这会将位从一个字节传送到下一个字节) – Cam 2010-03-06 23:05:18

回答

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个字节会产生对齐问题。例如,当从未对齐的字符数组中读取两个字节的字时,最好先进行奇数字节读取,以便稍后的字读取全部在偶数地址上,直到循环结束。

在x86上这不是必须的,但它要快得多。在一些处理器上是必要的。只需根据基地址(地址& 1),(地址& 3)或(地址& 7)来开关一个开关,以便在循环开始前处理前几个字节。您还需要特殊情况下主循环后的尾部字节。

+0

完美,谢谢!我会把它们放在一个向量中;不知道为什么我没有想到:) 真的有帮助的答案! – Cam 2010-03-06 23:25:29

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 ]; 
} 
+0

这看起来很酷。然而,作为一种学习体验,我很乐意制作我自己的作品。另外,我特别喜欢它能够根据需求扩大(大小)。 – Cam 2010-03-06 23:11:33

+0

@incrediman:请注意,boost :: dynamic_bitset对象的大小可以在运行时指定。如果你热衷于学习,我建议看看他们的实现并推出你自己的(只有你需要的功能)。 – dirkgently 2010-03-06 23:14:10

0

运算符重载是语法糖。这实际上只是一种调用函数并传递字节数组而不需要它的方式看起来就像您正在调用函数一样。

所以我写此功能

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

然后,如果你想在一个操作符重载来包装这件事,以使其更容易使用,或者因为你只是喜欢这种语法,你可以做启动也是如此。或者你可以调用这个函数。

+0

您只能在用户定义的类型上使用运算符重载,本示例为'unsigned char *'。海报必须定义一个类来表示数据结构。 – 2010-03-06 23:22:15

+0

@David:良好的捕获,无符号字符,不是字节。但我不确定我是否明白你的意思。为了实现超载,你仍然需要一个可以进行字节转换的函数。 – 2010-03-06 23:25:10

+0

'unsigned char'对于表示一个字节很有用,但它不是用户定义的类型。你不能定义'unsigned char * :: operator >>(int)',它必须是'Bitarray :: operator >>(int)'。中间有一步你错过了,我可能会得到有关海报细节的肛门。 – 2010-03-07 03:32:32

1

这是我会怎么做它的两个字节:

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

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

从那里,你可以概括成一个圈这为n个字节。为了灵活性,您需要生成幻数(0x3和6),而不是硬编码它们。

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; 
} 

与它刚玩。你会得到我相信的想法。