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

我看过什么是按位移位(位移)运算符以及它们如何工作? 但我仍然觉得比特移位的概念很难理解。

有人能指出我在C中进行位移的更基本指导的方向。我希望它会很长,因为它需要涵盖整个主题。

我真的不明白,但我想学习它,所以任何帮助将不胜感激。

我正在学习k&r,这就是这个,所以我可以做练习。 我理解基础知识,但我仍然无法做正确的位移操作。

编辑这里是K&R的表现令我难过的

练习2-6:写一个函数setbits(x,p,n,y),它返回x,其中n位位于位置p,位于y的最右边n位,其他位保持不变。

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

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

练习2-9:在二进制补码系统中,x&=(x-1)删除x中最右边的1位。 解释原因,使用此观察来编写更快的bitcount版本

这些是来自k&R(c编程语言)书籍的练习,它是最好的c书,但我无法理解比特档,所以我遇到了这些练习的问题。

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

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

(7 >> 1) 

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

我认为您引用的链接中的解释非常清楚。 也许你自己在纸上写下一系列的比特并按引用的链接操纵它们可以提供帮助。

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

您需要在工具包中使用更多工具来回答这些问题,而不是转移。

我想你需要开始非常基本的数字如何用二进制表示。 然后,您将考虑如何设置和清除该数字中的特定位或同时清除一组位。 您需要知道如何测试以查看是否设置了某组位和屏蔽。 您必须能够使用按位运算符和/或/ xor / inversion / etc运算符执行上述所有操作。 然后你会想知道移位 – 左右移动特定数量的空格。 你会想知道“空”的位会发生什么。 是填充1还是0? 什么时候填充1或0?

谷歌搜索“按位操作教程”似乎已经提出了一些有希望的结果。 但是这里有一些基础来测试自己,以确保你理解

 // 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\n", 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); 

祝好运!

将这些位写在纸上并考虑从一端擦除它们并在另一端添加更多。 与小学不同,当乘以10时移动小数点。

你的所有C函数都将移入零。

所以

 x = y << 3; 

意味着左移三位而右边的新位都是零,左边的三位进入“位桶”

 x = z >> 2 

丢失右边的两位,并在左边添加两个零。

您会发现什么以及K&R练习的内容是缺少的function,在处理器类型中,您具有比在C或任何其他高级语言中更多的转换function。

你有旋转function,其中一端移位的位移到另一端,

因此,以这种方式向右旋转1位的数字0xD将是0xE,因为lsbit是1,因此向右移位1101,右边的1变为左边的1 1110

有时你会旋转alu中的进位。 让我们说进位在其中有一个零,你旋转0xD一位0 1101离开1 0110一个0x6。 再旋转一个0 1011,你得到一个0xB,依此类推。

你为什么要旋转你问的进位? 对于更大的数字,假设您有4位寄存器并且想要进行8位移位,可以说每个字母都是位bcde fghi,其中a是进位位,另外两组是4位寄存器,开始于通过进位e abcd fghi旋转左侧寄存器,然后通过进位i abcd efgh旋转右侧寄存器,非常酷,我们只用8位移位和4位移位function。 如果你在开始之前已经清除了进位(通常有一个指令,或者你总是可以做一些像添加0 + 0或其他保证清除该位的东西)你会得到ibb efgh这与C不同如果你说的是在64位数字上运行的32位指令集,那么移位function就可以了。

处理器通常具有C移位,其中零移位,移位abcd左移一个给出bcd0移位abcd右两个给出00ab。

这导致了一些问题,现代处理器的年轻人不会考虑这样的事情,因为他们的处理器支持整数除法,并且可以在一个时钟周期内运行,在我们分频之前或者分频为几十到几百个时时钟,但一个class次是一个单一的时钟,你将使用移位intead做你所有的2分频或倍数的功率。 取数字0x0D左移两个得到0b00001101 << 2 = 0b00110100或0x34 0xD是13十进制,0x34是52十进制。 52是4的四倍。四是2的功率2.换2乘以4乘以相同。 这两种方式都有效,0x34右移2是0xD,但是这里有问题,当你进入负数时,取数字减去4 0xFC,现在除以2。 使用C 0xFC >> 1将给出0x7E但0x7E是+126十进制如何-4/2 = 126? 问题是C变为零。 您会发现某些处理器的算术移位与逻辑移位不同,算术移位保持最高位,因此如果您正在处理像0bQWER这样的有符号数,并且您算术右移一位,则得到0bQQwe,最高位都转移到下一位并保持原位。 再次转移0bQQQW,依此类推。 现在算术左移将以零而不是lsbit移位,因此0bQWER向左移位一个是0bWER0。 这有意义-4左移一个是0xF8,这是-8,-4乘以两个是-8所以这是正确的。 所以你会发现一些处理器只有一个算术右移而不是一个左,有些允许你指定一个asl但是当它们组装时它用一个lsl(逻辑左移)代替它,谁知道有些可能实际上有一个单独的操作码即使它是相同的function。 我假设可能有一些有asl和asr和lsr但没有lsl。

只需使用纸和笔,然后解决问题,从实数开始作为例子,然后抽象。 想要将0x1234旋转到右边一位,让我们说吧?

 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 

我想现在向右移两位

 0000100100011010 xx0000100100011010 1000001001000110 

如何在C中进行单位旋转?

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

要旋转更多,您可以简单地多次调用此函数,或者考虑掩码并转移到上面,以及如何工作多个位。

还要注意一些处理器只有一个旋转function,例如考虑一下我有一个4位寄存器而我旋转5位我得到了什么?

 abcd bcda first rotate cdab second dabc third abcd fourth bcda fifth 

单个旋转左边的样子是什么样的?

 abcd bcda one bit left. 

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

对于某些人而言,逻辑位操作与指示理解一样困难,但它是一个基础,如果你学习并使用它,你将在竞争对手或你周围的人之前走出路。

这是一个计算某个变量中位数的示例:

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

理解这一行代码,您就可以顺利学习C和逻辑位操作。

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

x = 17或10001

p = 4

n = 3

y = 15或01111

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

1st:将所有1移位到左侧n位置~0 << n = 1111111111111000

第二步:将所有1个放在最右边的n个位置〜(~0 << n)= 0000000000000111

3rd:将这n个位移到位置p,并从位置p开始将n位设置为零。 〜(〜(~0 << n)<<(pn))= 1111111111110001

第4:这个掩码用x来清除位置p = 10001处的n位x

(我确信每个人都意识到我们所做的就是回到我们的起始编号,但我们必须这样做才能告诉程序我们想要从哪个位置开始,以及我们想要使用多少位如果p为6且n为4,该怎么办?)

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

1st:〜(〜0 << n)将所有的一个放在最右边的n个位置。 0000000000000111

2nd:y&〜(〜0 << n)选择y 0000000000000111的最右边n位

第3:(y&〜(〜0 << n))<<(pn)将y位置于位置p 0000000000001110最后或将两个结果放在一起

0000000000010001 = 17

0000000000001110 = 7 OR =

0000000000011111 = 31

结果变成:

return x&〜(〜(~0 << n)<<(pn))| (y&〜(~0 << n))<<(pn);

如果这很难读,我很抱歉。 我的格式化能力有点受限。 (我确信这是我的错)我希望这有帮助,我一直坚持这个特定的练习很长一段时间,最后在看了这个线程之后,今天想出了这个解决方案,所以我想我会发布正确的答案,以及解释为什么它是正确的答案,这是我发现在我看到的许多答案中缺乏的东西。

您链接的指南非常好。 什么是你不了解的位移?

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

你能否更具体地回答这个问题,我可以编辑以更具体地回答这个问题。

如果你有钱(和时间),请抓一份Hackers Delight。 它可能是最好的位移指南。 它将带您完成简单的移位(以及其他操作技术),直到灰色代码等等……

我们来做一个练习,一个样本。

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

让我们保持最低有效位是位0(位于0)

这是伪代码:

  1. 将位p移动到x中的p + n到0到n的位置。 这就是你右移的地方。
  2. 准备一个位掩码,将从位n到最高位转换为0。 您可以使用查找数组(例如,对于n = 8为0xFF,对于7为0x7F,依此类推),或者您可以在循环中使用设置位0和lef-shifting的组合。
  3. 使用&将位掩码应用于x。 我们在x中不关心的所有位现在都是0
  4. 反转位掩码。 我们现在在高端有一堆1,在低端有一堆0。 我们想要改变的位在位掩码中都是0。
  5. 使用&将位掩码应用于y。 我们将用x中的位替换的y中的所有位现在都设置为0,并且我们不想更改的位不变。
  6. 通过使用|组合x和y来设置我们想要在y中更改的位 。 这是至关重要的一点 – 用笔和纸来回顾正在发生的事情。 在(3)中设置为0的x中的位不会影响y中的相应位。 x中不受(3)影响的位将与y中的0结合(因为步骤5为0),将结果位设置为x中的值。

请记住,几乎所有这些练习都有一个简单的解决方案,可以使用函数int IsBitSet(int i,int n)编写; int SetBit(int i,int n); 和<<和>>的组合。 直接的解决方案几乎都是最糟糕的情况,但更容易实现和阅读。 这有点像在增量方面在加法或加法方面实现乘法。

这里有关于堆栈溢出的按位运算符的很好的介绍: 这里

让我们尝试2-6,让您体验位操作的工作原理,然后看看它是否有意义。

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

注意:上面的内容可能完全不正确,因为我还没有测试过,但它应该给你一个不错的想法。

比特扭曲黑客

这是我在AVRFreaks找到一些时间的基本指南。 这将向您展示基础知识,然后您可以继续学习其他教程。