如何将字节数组移位12位

Jus*_*ner 12 c arrays bit-shift bitset

我想将字节数组的内容向左移12位.

例如,从这个类型的数组开始uint8_t shift[10]:

{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0xBC}
Run Code Online (Sandbox Code Playgroud)

我想将它向左移12位,结果是:

{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xC0, 0x00}
Run Code Online (Sandbox Code Playgroud)

Mik*_*tak 8

欢乐指点!

此代码通过向前看每个字节的12位并向前复制正确的位来工作.12位是下一个字节的下半部分(nybble)和2个字节的上半部分.

unsigned char length = 10;
unsigned char data[10] = {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0A,0xBC};
unsigned char *shift = data;
while (shift < data+(length-2)) {
    *shift = (*(shift+1)&0x0F)<<4 | (*(shift+2)&0xF0)>>4;
    shift++;
}
*(data+length-2) = (*(data+length-1)&0x0F)<<4;
*(data+length-1) = 0x00;
Run Code Online (Sandbox Code Playgroud)

贾斯汀写道:
@Mike,你的解决方案有效,但不能随身携带.

好吧,我会说正常的移位操作就是这样(称为溢出),只是让额外的位从右边或左边掉下来.如果您愿意,它很容易携带 - 只需在开始移动之前保存12位.也许你想要一个循环移位,将溢出的位置放回到底部?也许你想重新分配数组并使其更大?将溢出返回给调用者?如果非零数据溢出,则返回布尔值?你必须定义什么对你有意义.

unsigned char overflow[2];
*overflow = (*data&0xF0)>>4;
*(overflow+1) = (*data&0x0F)<<4 | (*(data+1)&0xF0)>>4;
while (shift < data+(length-2)) {
    /* normal shifting */
}  
/* now would be the time to copy it back if you want to carry it somewhere */
*(data+length-2) = (*(data+length-1)&0x0F)<<4 | (*(overflow)&0x0F);
*(data+length-1) = *(overflow+1);  

/* You could return a 16-bit carry int, 
 * but endian-ness makes that look weird 
 * if you care about the physical layout */
unsigned short carry = *(overflow+1)<<8 | *overflow;
Run Code Online (Sandbox Code Playgroud)


Mar*_*son 5

这是我的解决方案,但更重要的是我解决问题的方法。

我通过以下方法解决了这个问题

  • 绘制内存单元并绘制从目的地到源的箭头。
  • 制作了一个表格显示上面的图。
  • 用相对字节地址标记表中的每一行。

这向我展示了模式:

  • iL为 的低位 nybble(半字节)a[i]
  • iH成为高半字节a[i]
  • iH = (i+1)L
  • iL = (i+2)H

该模式适用于所有字节。

翻译成C语言,这意味着:

a[i] = (iH << 4) OR iL
a[i] = ((a[i+1] & 0x0f) << 4) | ((a[i+2] & 0xf0) >> 4)
Run Code Online (Sandbox Code Playgroud)

我们现在再提出三点观察:

  • 由于我们从左到右执行赋值,因此不需要在临时变量中存储任何值。
  • 尾部有一个特殊情况:12 bits最后的所有值都为零。
  • 我们必须避免读取数组之外的未定义内存。由于我们从未读取超过 的内容a[i+2],因此这只影响最后两个字节

所以,我们

  • N-2 bytes通过循环 for并执行上面的一般计算来处理一般情况
  • 通过设置来处理倒数第二个字节iH = (i+1)L
  • 通过将最后一个字节设置为来处理它0

给定alength N,我们得到:

for (i = 0; i < N - 2; ++i) {
    a[i] = ((a[i+1] & 0x0f) << 4) | ((a[i+2] & 0xf0) >> 4);
}
a[N-2] = (a[N-1) & 0x0f) << 4;
a[N-1] = 0;
Run Code Online (Sandbox Code Playgroud)

就这样……数组左移了12 bits。我相信,它可以很容易地推广到 moving N bits,注意到 where 会有M赋值语句M = number of bits modulo 8

通过转换为指针,可以在某些机器上提高循环的效率

for (p = a, p2=a+N-2; p != p2; ++p) {
    *p = ((*(p+1) & 0x0f) << 4) | (((*(p+2) & 0xf0) >> 4);
}
Run Code Online (Sandbox Code Playgroud)

并使用CPU支持的最大整数数据类型。

(我刚刚输入了这个,所以现在是某人检查代码的好时机,特别是因为位调整非常容易出错。)