K& R练习2-7中的位反转功能

时间:2013-04-01 16:19:15

标签: c bit-manipulation kernighan-and-ritchie

C编程语言的练习2-7:

  

写一个函数invert(x,p,n),返回x,其中n位从位置p开始反转(即1变为0,反之亦然),离开其他人不变。

我理解这样的问题:我有182个二进制101(101)10,括号中的部分必须反转而不改变其余部分。返回值应该是10101010,然后是十进制的170。

这是我的尝试:

#include <stdio.h>

unsigned int getbits(unsigned int bitfield, int pos, int num);
unsigned int invert(unsigned int bitfield, int pos, int num);

int main(void)
{
    printf("%d\n", invert(182, 4, 3));
    return 0;
}

/* getbits: get num bits from position pos */
unsigned int getbits(unsigned int bitfield, int pos, int num)
{
    return (bitfield >> (pos+1-n)) & ~(~0 << num);
}

/* invert: flip pos-num bits in bitfield */
unsigned int invert(unsigned int bitfield, int pos, int num)
{
    unsigned int mask;
    unsigned int bits = getbits(bitfield,pos,num);

    mask = (bits << (num-1)) | ((~bits << (pos+1)) >> num);

    return bitfield ^ mask;
}

对我来说似乎是正确的,但invert(182, 4, 3)输出536870730getbits()工作正常(直接来自本书)。我写下了我分配给y的表达式中发生的事情:

(00000101 << 2) | ((~00000101 << 5) >> 3)    -- 000000101 is the part being flipped: 101(101)10
       00010100 | ((11111010 << 5) >> 3)
       00010100 | (01000000 >> 3)
       00010100 | 00001000
= 00011100

  10110110 (182)
^ 00011100
----------
= 10101010 (170)

应该是正确的,但事实并非如此。我发现这是错误的地方:((~xpn << (p+1)) >> n)。我不知道怎么做。

另外,我不知道这段代码有多普遍。我的首要任务是让这个案例有效。也欢迎这个问题的帮助。

5 个答案:

答案 0 :(得分:4)

((1u<<n)-1)是RHS的n'1'位的位掩码。 <<p将这个p个位置的块移到左侧。 (如果你想从左边算起,你应该用(p-n)而不是p来移动。

return val  ^ (((1u<<n)-1) <<p) ;

当p大于单词大小时(仍然未定义单词大小的移位)仍然存在问题,但这应该是调用者的责任; - )

对于p = 2且n = 3的示例101(101)10

1u<<n               := 1000
((1u<<n)-1)         := 0111
(((1u<<n)-1) <<p) := 011100
original val    := 10110110
val ^ mask      := 10101010

答案 1 :(得分:1)

我认为你在其中一个转变中有一个一个一个问题(它只是一个预感,我并不完全确定)。然而,我保持简单(我假设索引位置p从LSB开始,即p = 0是 LSB ):

unsigned int getbits(unsigned int x, int p, int n) {
  unsigned int ones = ~(unsigned int)0;
  return x ^ (ones << p) ^ (ones << (p+n));
}

编辑:如果你需要p = 0作为 MSB ,只需反转移位(这可以正常工作,因为ones定义为unsigned int):

unsigned int getbits(unsigned int x, int p, int n) {
  unsigned int ones = ~(unsigned int)0;
  return x ^ (ones >> p) ^ (ones >> (p+n));
}

注意:在p < 0p >= sizeof(int)*8p+n < 0p+n >= sizeof(int)*8这两种情况下,getbits的结果都未定义。

答案 2 :(得分:0)

看一下Steve Summit的"Introductory C programming"和Ted Jensen的"At tutorial on pointers and arrays in C"。他们所涵盖的语言与今天的C语言略有不同(编程习俗已经发展,机器很多更大,真正的男人不再编写汇编程序了),但他们所说的很多内容都是真的今天就像那时一样。肖恩安德森的"Bit twiddling hacks"会使你的眼睛膨胀。保证。

答案 3 :(得分:0)

我发现我的实施中出了什么问题(除了从错误的方向计算num之外)。之后看起来相当明显,我已经学到了更多关于比特的知识。

当1位向左移位时,超出位域范围,它会扩展。

   1000 (8) << 1
== 10000 (16)

bitfield << nbitfield乘以2 n次。我的表达式((~bits << (pos+1)) >> num)分别为bitsposnum的值为5,4和3。我将一个几乎与32位int大小相乘的数字乘以2,两次。

答案 4 :(得分:0)

我的功能怎么样?我认为这很好。

unsigned invert(unsigned x,int p,int n)
{
     return (x^((~(~0<<n))<<p+1-n));
}