关于位移:我在哪里可以找到C的位移指南?

Where can I find a bit shifting guide for C?

我已经研究了什么是位移位(bit-shift)操作符,它们是如何工作的?但是我仍然觉得钻头移动的概念很难理解。

有人能给我指出一个更基本的C语言位移指南的方向吗?我希望它会很长时间,因为它需要涵盖整个主题。

我真的不明白,但我想学它,所以任何帮助都会受到感激。

我正在学习K&R,这就是我做练习的目的。我了解基本知识,但我仍然不能正确地进行位移操作。

编辑这是K&R给我的练习

练习2-6:写一个函数set bits(x,p,n,y),返回x,其中n位从p开始,设置为y的最右边n位,保持其他位不变。

练习2-7:编写一个函数invert(x,p,n),返回x,其中n位从poititon p inverted开始(即1变为0,反之亦然),其余的不变。

练习2-8:编写一个函数rightrot(x,n),它返回整数x的值,x向右旋转n位位置

练习2-9:在2的补码系统中,x&;=(x-1)删除x中最右边的1位。解释原因,使用这个观察结果来编写更快版本的比特计数。

这些是K&R(C编程语言)书中的练习,它是最好的C书,但我无法理解位移,所以我在这些练习中遇到了问题。


位移位只不过是字面意思:将给定序列中的所有位向左或向右移位。

你要记住的是,每一个十进制数字(如6、7、3、2)都被表示为计算机内存中的一个位序列。所以,如果你在一段C代码中找到类似的东西:

1
(7 >> 1)

这意味着7的底层二进制表示中的位将向右移动1个位置。

我认为你引用的链接中的解释非常清楚。也许你自己在纸上写下一系列的数据位,然后像引用链接中那样操作它们可以有所帮助。

或者你可能还不知道计算机如何在内部处理数字。在这种情况下,在学习比特移位之前,您需要阅读相关内容。


你需要更多的工具来回答这些问题,而不是转移。

我认为你需要从非常基本的角度来了解数字是如何用二进制表示的。然后,您需要考虑如何设置和清除该数字中的特定位,或者同时清除一组位。你需要知道如何测试,看是否设置了一组特定的位,以及如何屏蔽。您必须能够使用位运算符和/或/xor/inversion/etc运算符完成以上所有操作。然后你会想知道移位——将比特左右移动一定数量的空格。你会想知道剩下的"空"位会发生什么。它是用1还是0填充的?什么时候用1或0填充?

谷歌"位操作教程"似乎已经找到了一些有希望的结果开始。但这里有一些基本的东西要测试你自己,以确保你理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1234 in hex, you understand this is not 1234 in decimal? Do you understand
// how specifying in hex makes it easier to think about the binary representation?
unsigned short i = 0x1234;
// flip all the bits in 0x1234
printf("%04x", ~i);

// Test - Is the most significant bit set?
// mask is a binary number with the most significant bit set. Do
// you understand why this is so?
unsigned short mask = 0x8000;  
if ((mask & i) == mask)
{
    printf("bit set!");
}
else
{
    printf("bit cleared!");
}

// set the most significant bit
i = i | mask;
printf("Set the MSB of i, check it out: %i
"
, i)

// set the second most significant bit
// Do you see how by shifting mask right one it becomes the next most significant bit?
i = i | (mask >> 1);

祝你好运!


以2-6为例,我使用值17、4、3、15。所以:

x=17或10001

P=4

n=3

Y=15或01111

使用我的示例数字,我们需要做的是从y(111)中取出三个ls位,并从位置4开始将它们插入x中。这将给我们11111或31。我们需要从位置4开始清除x中的3个位,清除y中除最右边3个位之外的所有位,将它们移动到位置4和或两个值一起。

第一:全部向左N位~0<

第二:把所有的放在最右边的N个位置~(~0<

第三:将这些n 1位移动到位置p,并将n位设置为从位置p开始的零。~(~(~0<

第4个:这个带x的屏蔽,在p=10001的位置上清除x的n位。

(我确信每个人都意识到我们所做的一切就是回到我们的起点,但是我们必须这样做才能告诉程序我们要从哪个位置开始,以及我们要处理多少位。如果p是6,n是4怎么办?

接下来清除y中除最右边n位之外的所有位:

第一个:~(~0<

第二:y&;~(~0<

第3个:(y&;~(~0<

0000000000100001=17

00000000001110=7或=

000000000011111=31

结果是:

return x & ~(~(~0 << n) << (p-n)) | (y & ~(~0 << n)) << (p-n);

如果这本书很难读懂,我很抱歉。我的格式化能力有点有限。(我确信这是我的错)我希望这有帮助,我在这个特别的练习上坚持了很长一段时间,并且在看了这条线之后,今天终于想出了这个解决方案,所以我想我会贴出正确的答案,以及一个解释为什么它是正确的答案,这是我已经找到的答案。我见过很多答案。


把这些数据写在纸上,然后考虑从一端删除,在另一端添加更多数据。不像小学,十乘十的时候小数点会移动。好的。

所有的C函数都将移入0。好的。

所以好的。

1
x = y << 3;

意味着左移三位,右边的新位都是零,左边的三位进入"位桶"。好的。

1
x = z >> 2

丢失右边的两个位,在左边加上两个零。好的。

您将发现和K&R练习的内容是缺少的功能,在现有的处理器类型中,您拥有比C或任何其他高级语言更大的转移能力。好的。

你有旋转功能,其中一头的位移动到另一头,好的。

因此,以这种方式向右旋转1位的数字0xD是一个0xE,因为lsbit是1,所以向右移动1101,右边的1变成左边的1 1110。好的。

有时你通过进位在算术逻辑单元旋转。假设进位中有一个零,并且您旋转了一个位0 1101离开1 0110 A 0x6。再旋转一次0 1011,得到一个0xB等等。好的。

你为什么要通过你问的进位旋转?对于更大的数字,假设你有四位寄存器,并且想要做一个8位的移位,让我们假设每个字母都是位a bcde fghi,其中a是进位,另外两组4是四位寄存器,首先通过进位旋转左边的寄存器。e fang fghi然后通过进位旋转右寄存器我abcd efgh,很酷,我们刚刚做了一个8位移位和4位移位功能。如果您在开始之前清除了进位(通常会有一个关于这个的指令,或者您总是可以执行一些类似于添加0+0或其他保证清除该位的操作),那么您将i 0bcd efgh,这与C移位函数在32位指令集上操作64位数字时所做的操作不同。好的。

处理器通常具有C类移位,其中0移位,shift abcd left one给出bcd0 shift abcd right two给出00ab。好的。

这就导致了一些问题,现代处理器的年轻人不会考虑这样的事情,因为整数除法在他们的处理器上都支持,并且可以在一个时钟周期内运行,在我们进行除法之前,或者当除法是几十到数百个时钟时,但是一个时钟的移位就是一个时钟,你可以做你所有的2d功率。使用移位整数进行Ivides或乘法运算。取左边两个移位的数字0x0d,得到0b000001101<<2=0b00110100或0x34 0xd为13十进制,0x34为52十进制。52是13的四倍。四比二等于二。移动2等于乘以4。这两种方法都有效,0x34右移2是0xd,但这里有一个问题,当你进入负数时,取这个数减去40xfc,然后除以2。使用c 0xfc>>1将给出0x7e,但0x7e是+126十进制- 4/2=126?问题是C的移动是零。你会发现一些处理器的算术移位不同于逻辑移位,算术移位保持最高位,所以如果你使用一个带符号的数字,比如0BQWER,并且算术移位到正确的一位,你会得到0BQWE,最高位都会移位到下一位,并停留在原来的位置。再次切换0BQQW,依此类推。现在算术左移将以零而不是lsbit移动,所以0左移1是0。这是有道理的-4向左移动一个是0xf8,它是-8,-4乘以2是-8,所以这是正确的。所以你会发现一些处理器只有算术右移而不是左移,有些处理器允许你指定一个asl,但是当他们组装它时,用一个lsl(逻辑左移)替换它,谁知道有些处理器实际上可能有一个单独的操作码,即使它是相同的函数。我想可能有一些有ASL和ASR和LSR,但没有LSL。好的。

只要用纸和笔,把事情弄清楚,以实数为例,然后进行抽象。比如说,要将0x1234向右旋转一位吗?好的。

1
2
3
0001001000110100  write out the bits
x0001001000110100 shift right one
0000100100011010  because this is a rotate fill in the new bit with the bit that fell off on the prior operation

现在想右移两位好的。

1
2
3
0000100100011010
xx0000100100011010
1000001001000110

我如何在C中进行一点旋转?好的。

1
2
3
4
5
6
7
8
unsigned int rotate_right_one ( unsigned int x )
{
  unsigned int y;
  y = x & 1;  //save the bit that is about to fall off the right
  x >> = 1;  //logical rotate one bit
  x |= y<<31; //this assumes that unsigned int is 32 bits.
  return(x);
}

要旋转得更多,您可以多次调用这个函数,或者考虑一下蒙版和上面的移动,以及它如何工作超过一位。好的。

还要注意,一些处理器只有一个旋转功能,例如,想想这个,我有一个四位寄存器,我旋转5位,我得到了什么?好的。

1
2
3
4
5
6
abcd
bcda  first rotate
cdab  second
dabc  third
abcd  fourth
bcda  fifth

单次向左旋转是什么样子的?好的。

1
2
abcd
bcda  one bit left.

四位寄存器的右边五个与左边一个5-4=1相同。像ASL一样,一些处理器允许您对操作进行编码,但是汇编程序使用nbits shift作为旋转量将该操作替换为另一个旋转操作。好的。

对于某些人来说,逻辑位操作就像指针一样难以理解,但它是一个基本的操作,如果你学习并使用它,你将远远领先于你的竞争对手或你周围的人。好的。

下面是一个计算某个变量中位数的示例:好的。

1
for(count=0,r=1;r;r<<=1) if(r&some_variable) count++;

了解这一行代码,您就可以很好地学习C和逻辑位操作了。好的。好啊。


这是我在Avrfraks上找到的一个基本指南。这将向您展示基本原理,然后您可以继续学习其他教程。

  • http://www.avrfreaks.net/index.php?name=pnphpbb2&file=viewtopic&t=37871

小捣蛋鬼


这里有一个关于栈溢出的位操作符的很好的介绍:这里


让我们尝试2-6,让您了解位操作是如何工作的,然后看看它是否有意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int setbits( int x, int p, int n, int y )
{
    int bitmask = ~0, y_right = 0;

    /* Ok, first let's get y's rightmost bits.
     * Take our bitmask, shift it left by n bits, leaving the least significant
     * n bits as 0. Then, flip the bitmask so the rightmost n bits become 1, and the
     * leftmost n bits become 0.
     *
     * Then, AND this bitmask with y, to yield y's n rightmost bits.
     */

    bitmask = ~( bitmask << n );
    y_right = bitmask & y;

    /*
     * Ok, now let's use y_right as our bitmask for x.
     * Shift y_right to the left to *begin* at position p.
     */

     y_right <<= p - n;
     x |= y_right;

     return x;
}

注:以上可能是完全错误的,因为我没有测试过,但它应该给你一个体面的想法开始。


请记住,几乎所有这些练习都有一个简单的解决方案,可以用函数int isbitset(int i,int n)、int setbit(int i,int n)和<<和>>组合编写。直截了当的解决方案几乎都是最坏的情况,但更容易实现和阅读。这有点像是用加法实现乘法,用增量实现加法。


我们来做一个练习,举个例子。

Exercise 2-6: Write a function
setbits(x, p, n, y) that returns x
with the n bits that begin at position
p set to the rightmost n bits of y,
leaving the other bits unchanged.

让我们在ind中保持最低有效位是位0(在位置0处)

以下是伪代码:

  • 将x中的位p移到p+n,移到0到n的位置。这就是右移的位置。
  • 准备一个位掩码,将从第n位到最高位的任何内容转换为0s。您可以为此使用查找数组(例如,对于n=8的0xff,对于7的0x7f等等),也可以在循环中使用设置第0位和lef移位的组合。
  • 使用&;将位掩码应用于X。在x中我们不关心的所有位现在都是0
  • 反转位掩码。我们现在有一堆1在高端,还有一堆0在低端。我们要在y中改变的位在位掩码中都有0。
  • 使用&;将位掩码应用于Y。我们将用x中的位替换y中的所有位,现在都设置为0,我们不想更改的位不变。
  • 使用组合x和y,设置要在y中更改的位。这是关键的一点——用纸和笔来检查正在发生的事情。在(3)中设置为0的x中的位不会影响y中的相应位。不受(3)影响的x中的位将与y中的0(由于步骤5,因此为0)合并,将结果位设置为x中的值。

  • 如果你有钱(还有时间),拿一份黑客快感的拷贝。这可能是最好的钻头移动指南。它将带您通过简单的转换(和其他操作技术)一路通过灰色代码等等…


    你链接到的指南真的很好。你不明白什么是位移?

    我不知道K&R是什么,所以我不能帮你。

    你能更具体地回答这个问题吗?我可以修改为更具体地回答这个问题。