面试 - 位操作

时间:2013-11-07 17:46:16

标签: c algorithm bit

我被要求实现invert(x,p,n),它返回带有n位的x 位置p反转(即1变为0,反之亦然),其他位置保持不变。

我的解决方案是:

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

我在网上发现了这个问题的解决方案:

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

对我而言,它看起来不正确 - 问题的正确和有效方法是什么

1 个答案:

答案 0 :(得分:4)

嗯,你的实施显然是不正确的;考虑p = 1, n = 2

x ^ (((1 << (n + 1)) - 1) << (p - n + 1))
x ^ (((1 << 3) - 1) << 0)
x ^ ((8 - 1) << 0)
x ^ 7

这会反转x的三个低位,而不是两个。我们可以通过改为使用:

来解决这个问题
return x ^ (1 << n) - 1 << p - n + 1;

(我摆脱了大量的虚假括号)。这还有一个角落案例的错误;如果呼叫者想要翻转除了一个比特之外的所有比特(即n == sizeof x * CHAR_BIT - 1)。我们假设int是32位并且作为示例:

x ^ (1 << n) - 1 << p - n + 1;
x ^ (1 << 31) - 1 << p - 31 + 1;
    ^^^^^^^^^
    ruh-roh!

不幸的是,这会调用未定义的行为(C11,§6.5.7第4段):

  

如果E1具有带符号类型和非负值,并且E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为未定义。

您可以通过使常量1无符号...

来修复
return x ^ (1U << n) - 1 << p - n + 1;

...但是当<{1}}时,你仍然有未定义的行为(即如果调用者想要翻转 all 这些位)(C11,§6.5) .7第3段):

  

如果右操作数的值...大于或等于提升的左操作数的宽度,则行为未定义。

您在网上找到的解决方案以相同的方式遭受未定义的行为。如果你真的想让所有的边缘情况过于迂腐地纠正,那你就需要按照以下方式做点什么:

n == sizeof x * CHAR_BIT

这是一种迂腐的矫枉过正吗?是。我是否希望有人将此作为面试情况的第一关?不,我希望他们能够聪明地讨论这里涉及的危险吗?是。