一.C语言中异或概述

   在C语言中,异或(XOR)是一种二进制运算,它对两个数字的对应位进行比较,如果这两个位不同,则结果为1;如果这两个位相同,则结果为0。异或运算符在C语言中是^。

1.异或运算有几个有趣的性质:

交换律:a ^ b = b ^ a

结合律:(a ^ b) ^ c = a ^ (b ^ c)

任何数与0异或都等于其本身:a ^ 0 = a

任何数与自身异或都等于0:a ^ a = 0

异或运算对同一个数进行两次,结果仍为原数:a ^ b ^ b = a(因为b ^ b = 0,然后a ^ 0 = a)

二.异或的应用

1.交换两个变量的值

  异或运算可以用来交换两个变量的值,而不需要使用临时变量。这是通过异或运算的性质实现的:任何数与自身异或结果为0,任何数与0异或结果为其自身,以及异或运算的交换律和结合律。

#include <stdio.h>  

int main() {  

    int a = 5, b = 10;  

    a = a ^ b;  

    b = a ^ b; // 此时b为原a的值  

    a = a ^ b; // 此时a为原b的值  

    printf("a = %d, b = %d\n", a, b); // 输出: a = 10, b = 5  

    return 0;  

}

2.奇偶性判断

  一个数与1进行异或运算,可以用来判断该数的奇偶性。如果结果为0,则该数为偶数;如果结果为1,则该数为奇数。

#include <stdio.h>  

int main() {  

    int a = 12345;  

    if ((a ^ 1) - a == 1) {  

        printf("%d 是偶数\n", a);  

    } else {  

        printf("%d 是奇数\n", a);  

    }  

    // 输出: 12345 是奇数  

    return 0;  

}

3.加密与解密

异或运算可以用于简单的数据加密和解密。由于异或运算的自反性,使用相同的密钥进行两次异或操作可以恢复原始数据。

#include <stdio.h>  

int main() {  

    unsigned char message = 0x89AB;  

    unsigned char key = 0xA5A5;  

    unsigned char secret = message ^ key; // 加密  

    unsigned char decrypted = secret ^ key; // 解密  

    printf("加密后: %X\n", secret);  

    printf("解密后: %X\n", decrypted); // 输出应与原始message相同  

    return 0;  

}

4. 查找出现奇数次的数字

在数组中,如果只有一个数字出现了奇数次,其他数字都出现了偶数次,可以使用异或运算来找到这个出现奇数次的数字。因为任何数与自身异或结果为0,所以出现偶数次的数字在异或运算中会相互抵消,最后剩下的就是出现奇数次的数字。

#include <stdio.h>  

int findOddNumber(int arr[], int len) {  

    int result = 0;  

    for (int i = 0; i < len; i++) {  

        result ^= arr[i];  

    }  

    return result;  

}  

  

int main() {  

    int arr[] = {1, 2, 3, 2, 1, 4, 4};  

    int len = sizeof(arr) / sizeof(arr[0]);  

    int oddNumber = findOddNumber(arr, len);  

    printf("出现奇数次的数字是: %d\n", oddNumber);  

    return 0;  

}

5.两个值的快速比较

  例如比较两个值是否相等时,一般我们使用这个 a==b,如果两个数相等 ,a ^ b 的结果为零。

if((a^b) == 0) {

  //a和b若相同则为true

}

6.数据备份

  使用异或也可以很容易实现多个数据的互相备份,假如有数据a、b、c,则d = a ^ b ^ c,然后把数据d备份下来。

当a丢失时,可使用d ^ b ^ c来恢复

当b丢失时,可使用d ^ a ^ c来恢复

当c丢失时,可使用d ^ a ^ b来恢复

  由此可见备份了一份数据d后,丢失了其他任何一个数据,都可以通过备份数据与其它数据异或恢复回来,磁盘阵列技术raid5的数据备份原理,用的就是这个特性。

7.奇偶校验

  首先先看一个例子理解下:判断一个二进制数中1的数量是奇数还是偶数。

  求10110101中出现1的数量是奇数还是偶数;可将10110101逐位异或操作,结果为1就是奇数个1,结果为0就是偶数个1。

  

  奇校验:原始码+1位校验位,总共有奇数个1;

  偶校验:原始码+1位校验位,总共有偶数个1。

  

  比如为原始码添加奇偶校验位:

  原始码:1101010

  判断1的个数是否为偶数:1^1^0^1^0^1^0=0,因为逐位异或的结果为0,所以1的个数为偶数,奇校验则在原数据末尾添1,变成11010101;偶校验则在原数据末尾添0,变成11010100。

8.对某些特定的位翻转

  由于不管是0或者1与1做异或操作后将得到原值的相反值,因此当我们需要翻转一个整数的某些位时,我们可以使用位异或运算来实现掩码操作,将对应的数据位进行翻转。

//定义一个整数n

int a = 0x0f; //二进制表示为00001111

//如果需要翻转a的第4和第5位,则定义掩码mask

int mask = 0x18; //二进制表示为00011000

//使用位异或运算翻转n的第3位和第4位

n = n ^ mask; //结果为二进制表示为00010111

在单片机编程中对GPIO输出控制LED的闪烁原理也是如此,定义了一个宏,直接通过GPIOA口对ODR寄存器进行操作,异或PIN2的位。实现引脚PA2输出高低电平的翻转控制LED闪烁。

// PA2引脚输出电平翻转

#define LED_TOGGLE() GPIOA->ODR ^= GPIO_PIN_2

9.例如判断两个int32类型的数据的符号是否相同,其中31是符号位的偏移量:

if (((a ^ b) >> 31) & 1) {

  "符号不相同!";

} else {

  "符号相同!";

}

08-13 17:02