使用shift-right按位运算符

时间:2017-05-16 00:45:46

标签: c bit-manipulation

我在C中编写一个简单的代码(仅使用逐位运算符),该指针采用指向无符号整数x的指针并翻转位于{n}位置n的位。整数的二进制表示法。该函数声明如下:

int flip_bit (unsigned * x, unsigned n);

假设n介于0和31之间。

在其中一个步骤中,我执行右移操作,但结果不是我所期望的。例如,如果我0x8000000 >> 30,我会得到0xfffffffe,结果分别为1000 0000 ... 00001111 1111 ... 1110二进制表示法。 (预期结果为0000 0000 ... 0010)。

我不确定我犯错误的方式或地点。任何帮助,将不胜感激。感谢。

编辑1:下面是代码。

#include <stdio.h>
#define INTSIZE 31

void flip_bit(unsigned * x,
          unsigned n) {

int a, b, c, d, e, f, g, h, i, j, k, l, m, p, q;
// save bits on the left of n and insert a zero at the end
a = * x >> n + 1;
b = a << 1;

// save bits on the right of n
c = * x << INTSIZE - (n - 1);
d = c >> INTSIZE - (n - 1);

// shift the bits to the left (back in their positions) 
// combine all bits
e = d << n;
f = b | e;

// Isolating the nth bit in its position
g = * x >> n;
h = g << INTSIZE;
// THIS LINE BELOW IS THE ONE CAUSING TROUBLE.
i = h >> INTSIZE - n;

// flipping all bits and removing the 1s surrounding 
// the nth bit (0 or 1)
j = ~i;
k = j >> n;
l = k << INTSIZE;
p = l >> INTSIZE - n;

// combining the value missing nth bit and 
// the one with the flipped one
q = f | p;
* x = q;
}

我在运行flip_bit(0x0000004e,0)时遇到异常行为。有关右移操作的行在其上方以大写形式发表评论。

这可能是一种较短的方法(不使用一千个变量),但这就是我现在所拥有的。

编辑2:问题在于我将变量声明为int(而不是unsigned)。然而,这是一个解决问题的可怕方法。 @old_timer建议返回*x ^ (1u << n),这要好得多。

3 个答案:

答案 0 :(得分:2)

#include <stdio.h>
int main ( void )
{
    unsigned int x;
    int y;
    x=0x80000000;
    x>>=30;
    printf("0x%08X\n",x);
    y=0x80000000;
    y>>=30;
    printf("0x%08X\n",y);
    return(0);
}

gcc on mint

0x00000002
0xFFFFFFFE

或者这个

#include <stdio.h>
int main ( void )
{
    unsigned int x;
    x=0x12345678;
    x^=1<<30;
    printf("0x%08X\n",x);
}

输出

0x52345678

答案 1 :(得分:2)

这里的问题是您正在对已签名的int执行右移。

来自C standard的第6.5.7节:

  

5 E1的结果&gt;&gt; E2是E1右移E2位位置。如果E1具有无符号类型,或者E1具有有符号类型和非负数   值,结果的值是商的整数部分   E1 / 2 E2 如果E1有签名类型和负值,则   结果值是实现定义的。

大胆的部分就是你的情况。每个中间变量都是int类型。假设您的系统对负数使用2的补码表示,则设置为高位的任何int值都将被解释为负值。

在这种情况下,您将看到的最常见的实现定义的行为行为(实际上这是gccMSVC都这样做)是如果在有符号值上设置了高位那么1将在右移中移入。这保留了值的符号并使x&gt;&gt;对于所有有符号和无符号值,n等于x / 2 n

您可以通过将所有中间变量更改为unsigned来解决此问题。这样,它们匹配*x的类型,你就不会向左推送1。

至于你翻转的方法,有一个更简单的方法。您可以使用^运算符,它是按位异或运算符。

来自C标准的第6.5.11节:

  

4 ^运算符的结果是位的异或(XOR)   操作数(即,当且仅当如果,则设置结果中的每个位   恰好是转换后的操作数中的一个相应位   设定)。

例如:

  0010      1000
^ 1100    ^ 1101
------    ------
  1110      0101

请注意,您可以使用它来创建位掩码,然后使用该位掩码翻转另一个操作数中的位。

因此,如果您要翻转位n,请取值1,左移n将该位移动到所需位置,然后将该值与目标值进行异或,以翻转该位:

void flip_bit(unsigned * x, unsigned n) {
    return *x = *x ^ (1u << n);
}

在这种情况下你也可以使用^=运算符,它将左边的右操作数进行异或,并将结果分配到左边:

return *x ^= (1u << n);

还要注意整数常量的u后缀。这导致常量的类型为unsigned,这有助于避免您遇到的实现定义的行为。

答案 2 :(得分:0)

我会这样做

*x^=1<<n;