浅谈C语言中异或运算符的10种妙用

目录

1、前言

2、基本准则定律

3、妙用归纳

4、总结


1、前言

        C语言中异或运算符^作为一个基本的逻辑运算符,相信大家都知道其概念:通过对两个相同长度的二进制数进行逐位比较,若对应位的值不同,结果为 1, 否则结果为 0。

        但是它在实际使用中到底会有哪些应用场景或者说是有在一些编程技巧中该如何使用,本文总结归纳了一些异或运算符的编程时实用方法思路以供参考,如有其它好用的功能,也可提出一起分享。


2、基本准则定律

        首先需要明确的是异或运算的几个基本准则定律,这些准则定律为异或运算的巧妙应用提供了基础。

  • 交换律:对于任意变量 a 和 b,a ^ b 等于 b ^ a。

  • 结合律:对于任意变量 a、b 和 c,(a ^ b) ^ c 等于 a ^ (b ^ c)。

  • 自反性:任何数与自身进行异或操作结果为0,即 a ^ a = 0。

  • 零元素性质:任何数与0进行异或操作结果为自身,即 a ^ 0 = a。

  • 分配律:a ^ (b & c) = (a ^ b) & (a ^ c),其中 & 表示按位与操作。

  • 奇偶性:一个数字和1做异或运算,可以判断它的奇偶性。如果结果为0,表示这个数字是偶数,如果结果为1,表示这个数字是奇数。


3、妙用归纳

        暂归纳了10种在编程中经常遇见的异或运算使用的方法。

(1)两个值的快速比较

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

if(a^b == 0) {//a和b若相同则为true 
}

(2)数据交换

        利用异或运算的性质可以进行两个数的交换,不会利用额外的变量。

void swap()
{ a=a^b;b=b^a;a=b^a;
}

        注意利用异或运算进行数据交换有个前提:要交换的两个数必须在在不同的内存,不然会出现问题。如果 a 和 b 是同一个地址呢?比如,我们调用的是 swap(a[i], a[j]),当 i == j时,利用异或运算进行数据交换是错误的。

        在这种情况下,我们第一步做的 a ^= b,实际上就是 a[i] ^= a[i]。这将直接让a[i]中的元素等于0,而丢失原本存在a[i]中的元素。后续这个元素就再也找不回来了。针对这个问题解决方案是在做这个交换之前,判断一下 a 和 b的地址是否相同。也可以把逻辑改变为,判断一下a和b是否相同。如果相同,则什么都不做。

(3)加减法

        如二进制加法01 + 01 = 10,而异或运算是01 ^ 01 = 00,它其实做了加法,但高位不进位,所以异或又称不进位加法。

        如二进制减法10 - 01 = 01,而异或运算是10 ^ 01 = 11,也可以看做个位0向高位借了一个1当2来用,但高位不减1,所以异或也可以称为不退位减法。

        利用异或的这个特性,只要再解决一下进位和退位问题,就可以实现加减法了。这也是为什么CPU里面都是做位运算的逻辑门电路,却能实现数值计算。

int plus(int a, int b) {int sum, addition;while (b != 0) {// 加法(未考虑进位)sum = a ^ b;// 进位值,二进制中1 + 1的场景才会进位,//a & b只有两个都为1,结果才是1,左移一位,就是进位值了addition = (a & b) << 1;// 赋值,下次循环将进位加到a里面来,直到进位等于0a = sum;b = addition;}return a;
}int subtraction(int a, int b){int sum, abdication;while (b != 0){// 减法(未考虑退位)sum = a ^ b;// 退位值,二进制中0 - 1的场景才会退位,//~a & b只有a=0,b=1,结果才是1,左移一位,就是退位值了abdication = (~a & b) << 1;// 赋值,下次循环将退位再从a中减掉,直到退位等于0a = sum;b = abdication;}return a;
}

(4)数据备份

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

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

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

(5)数据加解密

        在例如通信时需要对数据使用简单的加解密方法时,使用异或操作可以进行明文数据的加解密,。比如明文数据是message,密钥是key,加密后的数据是secret,确保通信发送方和通信接收方都存储了相同的key,key可以使用rand()生产一串随机数,则举例说明:

// 加密
secret = message ^ key
0x2C0E = 0x89AB ^ 0xA5A5
// 解密
message = secret ^ key
0x89AB = 0x2C0E ^ 0xA5A5

        美国数学家香农证明了只要满足以下两个条件,异或加密是无法破解的。

  • key的长度大于等于message。

  • key必须是一次性的,且每次都要随机产生。

        理由很简单,如果每次的 key 都是随机的,那么产生的secret具有所有可能的值,而且是均匀分布,无法从secret看出 message 的任何特征。也就是说,它具有最大的"信息熵",因此完全不可能破解。这被称为异或的"完美保密性"。为了让异或加密更可靠,可以添加一些其他操作,例如对数据进行循环位移或取反的操作。

(6)数据奇偶数判断

        由于异或运算要先转化为二进制数,而偶数的二进制数的最后一位必定是0,奇数的二进制数的最后一位必定是1。

        因此,要判断的数与1异或得到的结果 - 要判断的数==1,说明该数是偶数,反之是奇数!

int a = 12345; //a=123456
if ((a ^ 1) - a == 1) {"是偶数!";
} else {"是奇数!";
}

(7)出现奇偶次的找数问题

        一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这一个数?

        分析:利用异或性质a ^ a=0,则出现偶数的数字相异或为0,而出现奇数次的异或结果为本身,那么,将所有的数异或后,就是要找的这个出现奇数次的数。

int ReOddNum1(int arr[], int len)
{int re = arr[0];for (int i=1;i<len;i++){re = arr[i] ^ re;}return re;
}

        一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到这两个数?

        step1:将所有数异或,得到re=a^b;

        step2:提取re最右侧的“1”(取反,+1,自身相与);

        step3:将所有该位为“1”的数相异或,得到的数便为a或者b;

        step4:再次相与两个结果,得到另一个数。

int* ReOddNum2(int arr[], int len)
{int re = arr[0];int rr[2] = { 0,0 };//第一步:将所有数异或,得到a^bfor (int i = 1; i < len; i++){re = arr[i] ^ re;}//第二步:提取re最右侧的“1”(取反,+1,自身相与)int rightone = re&(~re + 1);//第三步:将所有该位为“1”的数相异或,得到的数便为a或者bint re1 = 0;for (int i = 0; i < len; i++){if ((arr[i]&rightone)!=0){re1 = arr[i] ^ re1;}    }rr[0] = re1;//第四步:再次相与两个结果,得到另一个数rr[1] = re1^re;return rr;  
}

(8)奇偶校验

        首先先看一个例子理解下:判断一个二进制数中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。

(9)对某些特定的位翻转

        由于不管是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

(10)数据符号判断

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

if (((a ^ b) >> 31) & 1) {"符号不相同!";
} else {"符号相同!";
}

4、总结

        总之,异或运算是一种重要的二进制运算,在实际代码编程中有着广泛的应用。需要深刻的去理解异或运算符的几条基本准则定律,就能够快速地完成数字的交换、计算、去重等操作,为我们在功能开发中提供便利。

        小tips:归根到底在布尔代数中,其实只需要与、或、非运算就可以实现所有其它运算,所以异或运算实际可以通过与、或、非实现,最直观的表示方式如下:

a ^ b = (~a & b) | (a & ~b)  

↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/823496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《构建高效ERP系统:数据库字段命名的艺术与实践》

当设计大型ERP系统的数据库时&#xff0c;合理的字段命名是至关重要的。良好的字段命名原则和实践可以提高数据库的可读性、可维护性和性能。本文将探讨在设计大型ERP系统数据库时应该遵循的字段命名原则&#xff0c;并结合实际案例进行说明。 清晰明了的命名&#xff1a;字段名…

CMMI认证是什么?如何确定CMMI认证的目标和范围

CMMI&#xff08;Capability Maturity Model Integration&#xff09;认证是一种用于评估和改进组织软件和项目管理过程的框架。它由美国国防部软件工程所&#xff08;SEI&#xff09;开发&#xff0c;旨在帮助组织提高其软件和项目管理的成熟度水平。 CMMI认证的意义在于&…

哪里有su材质库免费下载?

su材质库是一套草图大师的通用材质大全&#xff0c;包含多种不同类型的材质包和材质贴图&#xff0c;使得设计师能够轻松在电脑上进行直观的构思。对于需要免费下载su材质库的用户&#xff0c;可以尝试通过以下途径获取。 1. 官方网站查找&#xff1a;许多软件都会在官网上提供…

第十六篇:springboot案例

文章目录 一、准备工作1.1 需求说明1.2 环境搭建1.3 开发规范1.4 思路 二、部门管理2.1 查询部门2.2 删除部门2.3 新增部门2.4 修改部门2.5 RequestMapping 三、员工管理3.1 分页查询3.2 删除员工3.3 新增员工3.3.1 新增员工3.3.2 文件上传 3.4 修改员工3.4.1 页面回显3.4.2 修…

【数据结构】-- 栈和队列

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

二十一.订单分析RFM模型

目录 1.数据读取 2.数据清洗 3.可视化分析 做图吧 4.RFM模型 本次数据条数为: 51101 import pandas as pd import numpy as np 1.数据读取 #读取文件 df_data pd.read_csv("../data/dataset.csv",encoding"gbk") df_data#因为列标签都是英文,这里我…

关于分布式session的问题

分布式session只能存储在当前的tomcat中&#xff0c;所以比如下次负载均衡到其他服务器会出现问题 session同步&#xff0c;在一个session中存储后同步到其他的服务器&#xff08;耗时&#xff0c;影响性能&#xff09;信息不存在session中了&#xff0c;存cooking中&#xff0…

通讯录的实现(顺序表)

前言&#xff1a;上篇文章我们讲解的顺序表以及顺序表的具体实现过程&#xff0c;那么我们的顺序表在实际应用中又有什么作用呢&#xff1f;今天我们就基于顺序表来实现一下通讯录。 目录 一.准备工作 二.通讯录的实现 1.通讯录的初始化 2.插入联系人 3.删除联系人 4.…

LeetCode 454.四数相加II

LeetCode 454.四数相加II 1、题目 题目链接&#xff1a;454. 四数相加 II - 力扣&#xff08;LeetCode&#xff09; 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 <…

手机副业赚钱秘籍:让你的手机变成赚钱利器

当今社会&#xff0c;智能手机已然成为我们生活不可或缺的一部分。随着技术的飞速进步&#xff0c;手机不再仅仅是通讯工具&#xff0c;而是化身为生活伴侣与工作助手。在这个信息爆炸的时代&#xff0c;我们时常会被一种焦虑感所困扰&#xff1a;如何能让手机超越消磨时光的定…

TinyEMU源码分析之访存处理

TinyEMU源码分析之访存处理 1 访存指令介绍2 指令译码3 地址转换3.1 VA与PA3.2 VA转PA 4 判断地址空间范围5 执行访存操作5.1 访问RAM内存5.2 访问非RAM&#xff08;设备&#xff09;内存 6 访存处理流程图 本文属于《 TinyEMU模拟器基础系列教程》之一&#xff0c;欢迎查看其…

【AI】什么是Ai Agent

什么是AI Agent&#xff1f; AI Agent是指人工智能代理&#xff08;Artificial Intelligence Agent&#xff09;是一种能够感知环境进行自主理解&#xff0c;进行决策和执行动作的智能体。AI Agent具备通过独立思考、调用工具逐步完成给定目标的能力。不同于大模型的区别在于&…

OpenHarmony实战开发-如何使用屏幕属性getDefaultDisplaySync、getCutoutInfo接口实现适配挖孔屏。

介绍 本示例介绍使用屏幕属性getDefaultDisplaySync、getCutoutInfo接口实现适配挖孔屏。该场景多用于沉浸式场景下。 效果图预览 使用说明 1.加载完成后顶部状态栏时间和电量显示位置规避了不可用区域。 实现思路 1.通过setWindowLayoutFullScreen、setWindowSystemBarEn…

代码随想录训练营

Day23代码随想录 669.修剪二叉搜索树 1.题目描述 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有…

k8s 节点打污点

例如&#xff0c;给节点node-1打上一个名为special值为true&#xff0c;效果为NoSchedule的污点&#xff0c;命令如下&#xff1a; kubectl taint nodes node-1 specialtrue:NoSchedule 污点格式为“KeyValue:Effect”&#xff0c;Key和Value作为污点的标签&#xff0c;Value…

Hbase的简单学习一

一 Hbase的搭建与安装 1.1 安装 1.准备好文件&#xff0c;上传到Linux上 2.解压文件 tar zxvf hbase-2.2.7-bin.tar.gz -C ../ ../是解压到的路径 1.2 配置文件 1.配置环境变量 去etc/profile目录下 export HBASE_HOME/usr/local/soft/hbase-2.2.7 export PATH$PATH:$H…

-存储器-

1存储器简介 存储器是一种保存数据的硬件单元2存储器分类 2.1按照数据易失性 2.1.1易失性存储器【RAM-Random Access Memory】 2.1.1.1【SRAM-Static Random Access Memory】 用晶体管存储0、1。速度快&#xff0c;成本高&#xff0c;存储密度低。2.1.1.2【DRAM-Dynamic R…

.NET SignalR Redis实时Web应用

环境 Win10 VS2022 .NET8 Docker Redis 前言 什么是 SignalR&#xff1f; ASP.NET Core SignalR 是一个开放源代码库&#xff0c;可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。 适合 SignalR 的候选项&#xff1a; 需要从服…

GCDAsynSocket之TCP简析

GCDAsynSocket是一个开源的基于GCD的异步的socket库。它支持IPV4和IPV6地址&#xff0c;TLS/SSL协议。同时它支持iOS端和Mac端。本篇主要介绍一下GCDAsynSocket中的TCP用法和实现。 首先通过下面这个方法初始化一个GCDAsynSocket对象。 - (id)initWithDelegate:(id<GCDAsyn…

AI预测福彩3D第38弹【2024年4月17日预测--第8套算法开始计算第6次测试】

今天咱们继续测试第8套算法和模型&#xff0c;今天是第5次测试&#xff0c;目前的测试只是为了记录和验证&#xff0c;为后续的模型修改和参数调整做铺垫&#xff0c;所以暂时不建议大家盲目跟买~废话不多说了&#xff0c;直接上结果&#xff01; 2024年4月17日3D的七码预测结果…