C – 在两个数字之间切换一下

我刚试过这段代码:

void swapBit(unsigned char* numbA, unsigned char* numbB, short bitPosition)//bitPosition 0-x { unsigned char oneShift = 1 << bitPosition; unsigned char bitA = *numbA & oneShift; unsigned char bitB = *numbB & oneShift; if (bitA) *numbB |= bitA; else *numbB &= (~bitA ^ oneShift); if (bitB) *numbA |= bitB; else *numbA &= (~bitB ^ oneShift); } 

交换a和b的位位置x,但由于if()我认为有更好的东西。

当我看到这个:

 *numbB &= (~bitA ^ oneShift); 

我真的认为有一种更简单的方法。 如果你有东西给我,我会接受它:)

提前致谢

首先,您应该将数字中的相应位置设置为0 ,然后将其与实际位进行或运算,删除所有条件:

 *numbB &= ~oneShift; // Set the bit to `0` *numbB |= bitA; // Set to the actual bit value 

其他数字相同。

形成面具

 unsigned char mask = 1u << bitPosition; 

然后通过XOR交换算法获得对等组的愤怒。

 *numbA ^= *numbB & mask; *numbB ^= *numbA & mask; *numbA ^= *numbB & mask; 

请注意,当numbA == numbB时,这会失败。

单个位并不比任意位掩码容易,所以我们来谈谈它。 您始终可以使用1U << bitpos调用此函数。

如果两个值中的位位置相同,则两者中都不需要进行任何更改。 如果相反,他们都需要反转。

XOR,1翻转; XOR为0是无操作。

所以我们想要的是一个值为1的区域,输入之间有一点差异,其他地方都是0。 这正是a XOR b所做的。 简单地将其屏蔽为仅交换一些位,并且我们在3个XOR + 1 AND中进行位交换。

 // call with unsigned char mask = 1U << bitPosition; if you want inline void swapBit_char(unsigned char *A, unsigned char *B, unsigned char mask) { unsigned char tmpA = *A, tmpB = *B; // read into locals in case A==B unsigned char bitdiff = tmpA ^ tmpB; bitdiff &= mask; // only swap bits matching the mask *A = tmpA ^ bitdiff; *B = tmpB ^ bitdiff; } 

( 带有gcc for x86-64和ARM的Godbolt编译器浏览器 ,包括一个带有unsigned而不是unsigned char的版本。)

您可以考虑if(bitdiff) { ... } ,但除非您通过避免分配来避免弄脏内存中的缓存行,否则可能不值得做任何条件行为。 使用寄存器中的值(在内联之后),保存两个xor指令的分支几乎永远不值得。

不是xor-swap 。 它确实使用临时存储。 正如@ chux的答案所示,屏蔽的xor-swap需要3个AND操作以及3个XOR。 (并且通过要求临时寄存器或其他存储来取消XOR-swap的唯一好处。)

此版本仅需要1 AND。 此外,最后两个XOR彼此独立,因此从输入到两个输出的总延迟仅为3个操作。 (通常为3个周期)。


对于x86 asm示例,请参阅此代码 - 高尔夫交换大写字母的两个字符串,在14个字节的x86-64机器代码中 (带有注释的asm源代码)