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。好的。 所以好的。 意味着左移三位,右边的新位都是零,左边的三位进入"位桶"。好的。 丢失右边的两个位,在左边加上两个零。好的。 您将发现和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。好的。 只要用纸和笔,把事情弄清楚,以实数为例,然后进行抽象。比如说,要将 现在想右移两位好的。 我如何在C中进行一点旋转?好的。 要旋转得更多,您可以多次调用这个函数,或者考虑一下蒙版和上面的移动,以及它如何工作超过一位。好的。 还要注意,一些处理器只有一个旋转功能,例如,想想这个,我有一个四位寄存器,我旋转5位,我得到了什么?好的。 单次向左旋转是什么样子的?好的。 四位寄存器的右边五个与左边一个5-4=1相同。像ASL一样,一些处理器允许您对操作进行编码,但是汇编程序使用nbits shift作为旋转量将该操作替换为另一个旋转操作。好的。 对于某些人来说,逻辑位操作就像指针一样难以理解,但它是一个基本的操作,如果你学习并使用它,你将远远领先于你的竞争对手或你周围的人。好的。 下面是一个计算某个变量中位数的示例:好的。 了解这一行代码,您就可以很好地学习C和逻辑位操作了。好的。好啊。 这是我在Avrfraks上找到的一个基本指南。这将向您展示基本原理,然后您可以继续学习其他教程。 小捣蛋鬼 这里有一个关于栈溢出的位操作符的很好的介绍:这里
2
3
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
2
3
xx0000100100011010
1000001001000110
2
3
4
5
6
7
8
{
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);
}
2
3
4
5
6
bcda first rotate
cdab second
dabc third
abcd fourth
bcda fifth
2
bcda one bit left.
让我们尝试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处)
以下是伪代码:
如果你有钱(还有时间),拿一份黑客快感的拷贝。这可能是最好的钻头移动指南。它将带您通过简单的转换(和其他操作技术)一路通过灰色代码等等…
你链接到的指南真的很好。你不明白什么是位移?
我不知道K&R是什么,所以我不能帮你。
你能更具体地回答这个问题吗?我可以修改为更具体地回答这个问题。