我被要求实现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);
}
对我而言,它看起来不正确 - 问题的正确和有效方法是什么
答案 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
这是一种迂腐的矫枉过正吗?是。我是否希望有人将此作为面试情况的第一关?不,我希望他们能够聪明地讨论这里涉及的危险吗?是。