C中的异或运算符

4

我在进行位操作时,有些困惑何时使用异或运算符。按位与和按位或都很简单。当你想要屏蔽某些位时,使用按位与(常见用例是IP地址和子网掩码)。当你想要打开某些位时,使用包含或。然而,异或总是让我感到困惑,如果在面试中被问及需要使用异或的问题,我永远不会理解。有人能解释一下何时使用它以及一些常见用例吗?

5个回答

10
您可以使用异或运算来翻转位 - 打开的位将被关闭,关闭的位将被打开。例如,在没有第三个变量的情况下交换两个数字时,这非常方便。
0x0A ^ 0xFF = 0x03 ( 00001010 ^ 11111111 = 11110101 )

交换数字:

operation   example 
             A      B
initial:   0011    1010
a = a^b;   1001    1010
b = a^b;   1001    0011
a = a^b;   1010    0011

如您所见,这里的数字(在此情况下是四位二进制数)A和B被交换而没有使用额外的空间。这适用于相同类型的任何两个数字(尽管在C语言中,按位运算符需要无符号整数)

XOR操作也可用于“弱加密”。您可以将一个字符串与(重复的)“密码词”进行XOR运算,结果将是一串毫无意义的字节。再次应用相同的操作,原始字符串会出现。这是一种非常薄弱的算法,不应在现实生活中使用。

关于切换位 - 在“旧时代”,如果您想要反转图像,则需要为每个像素执行 pix = pix & 1 - 或者如果您可以每次以字节为单位执行,则为byte = byte & 0xFF。这将把黑色文本在白色背景上变成白色文本在黑色背景上。我认为ATARI有一个专利,可以通过使用位图和XOR来创建“屏幕上任何位置”的闪烁光标。

同样,如果您有一个微控制器希望创建闪烁的光,重复执行state = state XOR 1将导致状态切换。

最后,有许多“位操作技巧”依赖于XOR操作。例如,可以使用以下方法(源自:http://graphics.stanford.edu/~seander/bithacks.html)计算字的奇偶性(即设置位数是奇数还是偶数):

unsigned int v;  // word value to compute the parity of
v ^= v >> 16;
v ^= v >> 8;
v ^= v >> 4;
v &= 0xf;
return (0x6996 >> v) & 1;

还有许多其他的“聪明技巧” - 它们通常会在你试图找到涉及位操作的最快方法时出现。大多数人可以毫不费力地度过一生,而不必真正“理解”它,这很好。我喜欢位操作。


1
是的,与零进行异或运算不会产生任何影响,结果位保持不变。与1进行异或运算总是切换该位。 - Graeme
1
@AyBayBay 如果你有更多的经验并且处理更广泛的问题,你就会遇到这种情况。这是一个相对不常见的操作,所以如果你还没有遇到需要它的情况,不要担心。 - Jim Balter
1
祝你面试好运!我刚想到了XOR的另一个(非常实用的)用途 - 它在FFT算法中使用。例如,请参见http://hackage.haskell.org/package/feldspar-language-0.6.0.3/docs/src/Feldspar-Algorithm-FFT.html或(在C中)http://www.katjaas.nl/FFTimplement/FFTimplement2.html。 - Floris
1
XOR在密码学中被广泛使用。事实上,如果密钥是真正随机并且与明文一样长,一个简单的XOR加密是不可破解的。 - Norill Tempest
1
@NorillTempest - 但如果我没记错的话,这样的密钥是无法通信的,这使得它适用于加密学,但不适用于加密通信。它非常适合“共享秘密”-每个团队成员都会得到一个表面上随机的文件,只有所有文件的异或才有意义。非常适合确保“所有孩子都出现在遗嘱的阅读中”... - Floris
显示剩余2条评论

3

除了其他答案中解释的按位交换两个数字和位切换外,XOR 操作还用于在不使用 if 语句的情况下找到两个数字的最大值。

int max(int x, int y)
{
      return x ^ ((x ^ y) & -(x < y)); 
}

1
从未见过这个,真性感! - Keeler
“(x < y) 部分不是会使用条件分支编译吗?” - mcleod_ideafix
@Floris;我的意思是不使用if语句。现在已经更正了。 - haccks
很酷。这是比 #define MAX(a,b) ((a)>(b))?(a):(b) 更快吗? - Floris
1
@Floris; 抱歉,我对此一无所知。这是校园招聘面试中的一个问题。(让我想一想)。 - haccks

3

在创意方面,XOR操作通常用于根据给定点的当前X和Y坐标呈现简单的正方形图形。例如,类似于棋盘的图案,具有可变的单元格大小:

#include <stdio.h>

#define CELLSIZE 2   /* cell size is actually 2 power CELLSIZE */
#define MASK (1<<CELLSIZE)

int main()
{
    int i,j;

    for (i=0;i<32;i++)
    {
        for (j=0;j<32;j++)
            putchar (' '+('*'-' ')*( ((i&MASK)^(j&MASK))!=0 ) );
        putchar ('\n');
    }
    return 0;
}

    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
    ****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****
****    ****    ****    ****

在电子游戏编程中,如果没有硬件精灵和/或多个游戏场景,最简单的方法是在位图背景上绘制精灵,以便可以在方便时擦除它,而无需存储精灵修改的屏幕部分,这就是使用异或运算将精灵像素与背景像素(一个像素由一个比特编码)相混合。要擦除精灵,只需在相同坐标处再次绘制即可。此方法也可用于在某些非加速显示硬件上呈现鼠标指针。

2

我最喜欢的一个例子是原地交换。(注意:这是C++代码,而不是C代码,但你可以理解。

void swap(int &x, int &y)
{
    x ^= y;
    y ^= x;
    x ^= y;
}

您还可以切换单个位:(n是第n位,在b中被切换)
a = b ^ (1 << n)

另一个不太常见的用途:XOR链接列表:
  • 您可以保存下一个和上一个条目的异或结果,而不是保存每个指针。现在,我们拥有如此多的内存,可能并不重要,但嘿,这还是很酷的。(除非您在嵌入式系统上,并且由于某种原因您需要双向链表。然后您会很高兴您阅读了本文。)

使用按位异或来交换两个数字是巧妙的--但在这种情况下,这是一件坏事。首先,如果xy指向同一个对象,则会失败。只需使用临时变量即可。如果要交换的对象非常大,而您无法承担分配临时变量的成本,请使用循环逐个交换字节或字。 - Keith Thompson
至于异或链表技巧,那同样是聪明的。当你遍历它时,它会使列表处于不一致状态,并且这种指针位操作在严格意义上来说是未定义行为(尽管在大多数系统上可能可以逃脱惩罚)。 - Keith Thompson
1
xy不能指向同一个对象,它们都是整数。除非你这样调用swap(x, x),这什么也不做。 :) - Keeler
关于在指针上使用异或运算,我同意。不过现在想想,这实际上是令人恐惧的。然而,这只是一个有趣的事情,就像这里的大多数其他例子一样。 - Keeler
我的错误:xy不是指针,它们是语法错误。(该问题标记为C,而不是C++。) - Keith Thompson
真的!即使问题标题是C。哎呀!好吧,你们都明白了。 - Keeler

0

XOR 的一个应用可以在特定情况下看到,当给定一个数组,其中每个数字都重复出现,除了一个不重复的单一数字需要被找到时。对所有数字进行 XOR 运算将得到仅出现一次的数字。

int arr[]={1, 1, 2, 2, 3, 3, 4};
// frequency of every number in the array is 2 except one number.
int sz=7; //size of array
int x=0; 
for(int i=0;i<sz;i++)
   x^=arr[i];
// x would give the number 4 here 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接