代码实例看透位运算符 | ^ ~

news2026/4/13 15:08:38
要先理解原码补码反码可以看这个文章https://blog.csdn.net/2301_80428740/article/details/147284230?spm1011.2415.3001.10575sharefrommp_manage_link 在C语言中位运算符是直接操作变量二进制补码的“底层操作工具”核心作用是对两个或一个变量的二进制位逐位进行运算。它比移位运算符更灵活在数据处理、状态判断等场景中经常用到。今天我们就结合具体代码实例用通俗的语言拆解最常用的4个位运算符按位与、按位或|、按位异或^、按位取反~。先明确3个核心前提避免理解偏差一是所有位运算操作的都是变量的二进制补码计算机存储数据的真实形式二是运算后原变量的值不会改变只会生成新的运算结果三是除了按位取反~是单目运算符只需要一个操作数其余、|、^都是双目运算符需要两个操作数运算时会将两个操作数的二进制位按“对应位置”逐位运算。例子代码// | ^ ~ 位运算符 #include stdio.h int main() { int num1 3; int num2 -5; printf(%d\n, num1 num2); // 00000000000000000000000000000011 3的补码 // 10000000000000000000000000000101 -5的原码 //求-5的补码 // 11111111111111111111111111111010 取反 // 11111111111111111111111111111011 加1 -5的补码 //按位与全1才为1否则为0 // 00000000000000000000000000000011 3的补码 // 11111111111111111111111111111011 -5的补码 // 00000000000000000000000000000011 按位与后的补码 //发现符号位为0说明是正数所以结果为3。如果是 负数还需要再取反加1一次。 printf(%d\n, num1 | num2); printf(%d\n, num1 ^ num2); // ~3 结果验证代码 int x -3; int y ~x; printf(~3 的结果是%d\n, y); // 最终输出-4 printf(~-6的结果是%d\n, ~- 6); // 最终输出5 printf(%d\n, ~0);//特殊 return 0; }一、按位与全1才为1否则为0按位与的语法操作数1 操作数2核心规则只有一条将两个操作数的二进制补码按对应位对齐只有当对应两位都为1时结果位才是1只要有一位是0结果位就是0可以记为“同1为1一0则0”。我们直接看代码中的核心案例num13num2-5计算num1 num2int num1 3; int num2 -5; printf(%d\n, num1 num2); // 最终输出3要理解这个结果必须先明确两个数的二进制补码再逐位运算步骤拆解如下第一步求3的二进制补码。3是正数原码、反码、补码完全相同32位二进制为 00000000 00000000 00000000 00000011第二步求-5的二进制补码。负数的补码需要通过“原码→反码→补码”的步骤计算1. 原码最高位符号位为1其余位为数值的二进制即 10000000 00000000 00000000 000001012. 反码符号位不变其余位按位取反0变11变0即 11111111 11111111 11111111 111110103. 补码反码加1即 11111111 11111111 11111111 11111011第三步按位与运算。将两个补码的对应位对齐逐位应用“全1才为1”的规则num1补码 00000000 00000000 00000000 00000011num2补码 11111111 11111111 11111111 11111011逐位运算结果00000000 00000000 00000000 00000011第四步将运算结果的补码转换为十进制。运算结果的补码最高位是0说明是正数补码即原码对应十进制就是3——这就是代码中printf输出3的原因。补充一个实用场景按位与常用来“提取指定二进制位”或“将指定位清0”。比如要提取一个数的最低4位只需让它和00001111十进制15做按位与运算即可。二、按位或|有1就为1全0才为0按位或的语法操作数1 | 操作数2核心规则和按位与相反将两个操作数的二进制补码按对应位对齐只要对应两位中有一位是1结果位就是1只有当两位都为0时结果位才是0可以记为“一1则1同0为0”。我们继续用代码中的num13和num2-5计算num1 | num2代码中printf(%d\n, num1 | num2);步骤拆解如下第一步明确两个数的补码和按位与案例一致直接复用 num1补码00000000 00000000 00000000 00000011 num2补码11111111 11111111 11111111 11111011第二步按位或运算。逐位应用“有1就为1”的规则 num1补码00000000 00000000 00000000 00000011 num2补码11111111 11111111 11111111 11111011 逐位运算结果11111111 11111111 11111111 11111011第三步将运算结果的补码转换为十进制。运算结果的补码最高位是1说明是负数需要先转换为原码补码取反加1 1. 补码取反10000000 00000000 00000000 00000100 2. 加1得到原码10000000 00000000 00000000 00000101 原码对应十进制是-5所以num1 | num2的结果是-5。补充实用场景按位或常用来“将指定二进制位置1”。比如要将一个数的最低位设为1只需让它和00000001十进制1做按位或运算即可。三、按位异或^不同为1相同为0按位异或的语法操作数1 ^ 操作数2核心规则很有特点将两个操作数的二进制补码按对应位对齐当对应两位不同时一个0、一个1结果位就是1当对应两位相同时都是0或都是1结果位就是0可以记为“异则1同则0”。还是用num13和num2-5计算num1 ^ num2代码中printf(%d\n, num1 ^ num2);步骤拆解如下第一步复用两个数的补码num1补码00000000 00000000 00000000 00000011num2补码11111111 11111111 11111111 11111011第二步按位异或运算。逐位应用“不同为1相同为0”的规则num1补码00000000 00000000 00000000 00000011num2补码11111111 11111111 11111111 11111011逐位运算结果11111111 11111111 11111111 11111000第三步将运算结果的补码转换为十进制。结果补码最高位是1为负数转换为原码取反加11. 补码取反 10000000 00000000 00000000 000001112. 加1得到原码10000000 00000000 00000000 00001000原码对应十进制是-8所以num1 ^ num2的结果是-8。补充两个实用场景1. 按位异或可以实现“不借助临时变量交换两个数”a a ^ b; b a ^ b; a a ^ b;2.一个数和0做异或运算结果还是它本身一个数和自己做异或运算结果是0相同为0。四、按位取反~0变11变0按位取反是唯一的单目位运算符语法~ 操作数核心规则最简单将操作数的二进制补码每一位都翻转包括符号位——0变成11变成0可以记为“逐位翻转”。1 正整数按位取反结果必然是负数符号位从 0 翻转为 1结果 正整数的相反数 - 1通用公式~n -n - 1n 为正整数看代码中的案例计算~3int x 3; int y ~x; printf(~3 的结果是%d\n, y); // 最终输出-4~3的结果应该是-43的相反数是-3-3-1-42 负整数按位取反结果必然是非负数符号位从 1 翻转为 0结果 负整数的相反数 - 1通用公式仍成立~n -n - 1n 为负整数看代码中的案例计算~-6printf(~-6的结果是%d\n, ~- 6); // 最终输出53 0的按位取反是-1特殊printf(%d\n, ~0); // 最终输出-1步骤拆解如下第一步明确0的32位二进制补码。0的原码、反码、补码都是全0 00000000 00000000 00000000 00000000第二步按位取反运算。将每一位0都变成1得到 11111111 11111111 11111111 11111111第三步将运算结果的补码转换原码。1. 补码取反10000000 00000000 00000000 000000002. 加1得到原码10000000 00000000 00000000 00000001 原码对应十进制是-1所以~0的结果是-1。五、核心规则总结表必记运算符类型核心规则简单记忆按位与双目对应两位全1则为1否则为0同1为1一0则0|按位或双目对应两位有1则为1全0则为0一1则1同0为0^按位异或双目对应两位不同则为1相同则为0异则1同则0~按位取反单目每一位都翻转0变1、1变0逐位翻转练习题1不创建第三个变量实现两个整数的交换^方法一#includestdio.h int main() { int a 10; int b 20; printf(交换前a %d, b %d\n, a, b); // a 10, b 20 aab; b a - b; a a - b; printf(交换后a %d, b %d\n, a, b); // a 20, b 10 return 0; }方法二#includestdio.h int main() { int a 10; int b 20; printf(交换前a %d, b %d\n, a, b); // a 10, b 20 a a ^ b; b a ^ b; a a ^ b; printf(交换后a %d, b %d\n, a, b); // a 20, b 10 return 0; }关键特性也是这段代码能生效的基础自反性x ^ x 0任何数和自己异或结果为 0归零性x ^ 0 x任何数和 0 异或结果还是自己交换律 / 结合律a ^ b b ^ a、(a ^ b) ^ c a ^ (b ^ c)逐行拆解代码执行过程我们以初始值a10二进制00001010、b20二进制00010100为例一步步算第一步a a ^ b;计算10 ^ 20 00001010 ^ 00010100 00011110十进制30此时a 30b 20b 还没变化第二步b a ^ b;把第一步的a30代入本质是b (a原来 ^ b原来) ^ b原来根据异或结合律 自反性(a ^ b) ^ b a ^ (b ^ b) a ^ 0 a计算30 ^ 20 00011110 ^ 00010100 00001010十进制10此时a 30b 10b 已经变成了原来的 a第三步a a ^ b;此时的a30原 a^ 原 bb10原 a本质是a (a原来 ^ b原来) ^ a原来同理(a ^ b) ^ a b ^ (a ^ a) b ^ 0 b计算30 ^ 10 00011110 ^ 00001010 00010100十进制20此时a 20b 10a 变成了原来的 b最终实现了a和b的交换整个过程没有用到临时变量完全靠异或的特性完成。对比加减法交换 vs 异或交换新手必看特性加减法交换异或交换适用类型整数 / 浮点数仅整数二进制位运算风险点可能溢出比如 ab 超出 int 范围仅当 a 和 b 指向同一地址时出错底层逻辑加减法逆运算二进制位异或特性可读性更高更易理解较低需懂异或规则练习题2 求一个整数在内存中的二进制中1的个数这里我们将用到按位与运算符因为它的“全1才为1”规则能精准判断某一位是否为1。方法一取余除2法新手友好利用整数除以2的特性一个整数除以2本质是其二进制补码向右移动1位。通过“取余2”判断当前最右边的位是否为1若n%21则最右边位是1若n%20则是0统计完最右边位后将n除以2舍弃该位重复操作直到n变成0统计的次数就是1的个数。#includestdio.h int main() { int n 0; int count 0; scanf(%d, n); while (n ! 0) { if (n % 2 1) { count; } n n / 2; } printf(%d, count); return 0; }适用场景正数统计逻辑直观无需理解位运算细节 - 局限性 1. 无法直接统计负数负数在内存中存储的是补码全1开头如-1在内存中是全1而我们测试得到的结果为0可改进改为无符号类型编译器-1的全1认为正数有32个1unsigned int n 0; // 无符号整数方法二逐位右移1法逻辑清晰兼容正负负数的符号位也算1核心逻辑基于“右移运算”和“按位与运算”的结合1. 右移运算n i将n的二进制补码向右移动i位第i位从0开始计数从右往左会移动到最右边的位置2. 按位与运算11的二进制补码是“...0001”只有最右边一位是1。用右移后的结果与1做按位与若结果为1说明当前移动到最右边的位是1若为0则是0 3. 循环32次针对32位整数依次判断每一位是否为1统计1的总数。代码实现#includestdio.h int main() { int n 0; //思路 // n13 // 1101 // 0001 按位与 1101 0001 0001 可能得出最后一位是不是1 // nn1 n0110 // 0110 0001 0000 可能得出倒数第二位是不是1 int count 0; scanf(%d, n); for (int i 0; i 32; i) { if (((n i) 1) 1) { count; } } printf(%d, count); return 0; }代码中需注意运算符优先级右移运算符和按位与运算符的优先级低于比较运算符因此必须给“(n i) 1”加上括号否则会先计算“i 1”导致逻辑错误。方法三n (n-1)法高效简洁对于任意整数n执行n (n-1)运算会将n的二进制补码中最右边的一个1变成0其他位不变。每执行一次该运算就统计一个1直到n变成0所有1都被统计完循环的次数就是二进制中1的个数。举个例子理解n6 补码00000000 00000000 00000000 00000110n-15补码00000000 00000000 00000000 00000101执行n(n-1)00000000 00000000 00000000 00000100 4最右边的1第2位被变成0再执行一次n4n-134300000000 00000000 00000000 00000000最右边的1第3位被变成0。// 统计整数n的二进制补码中1的个数 int countOneBits(int n) { int count 0; // 用于计数1的个数 while (n ! 0) { // 当n为0时所有1都已统计 n n (n - 1); // 每次将最右边的1变成0 count; // 统计次数加1 } return count; } int main() { int n 0; scanf(%d, n); printf(%d, countOneBits(n)); return 0; }三种方法核心对比表对比维度取余除2法逐位右移1法n (n-1)法核心逻辑取余判断最右位除2舍弃最右位右移i位后与1按位与判断第i位是否为1n(n-1)清0最右1统计清0次数是否支持负数不支持会陷入死循环支持固定循环32次覆盖所有位支持直接操作补码循环效率低循环次数二进制位数最多32次中固定循环32次高循环次数1的个数最少1次理解难度低依赖基础算术运算中需理解右移和按位与的结合中高需理解n(n-1)清0最右1的特性适用人群新手刚接触整数二进制表示有基础的学习者理解移位运算掌握位运算核心特性的学习者/开发者练习题三判断整数是否为 2 的 n 次方方法1#includestdio.h int main() { int a 0; scanf(%d, a); // 核心判断逻辑 // 1. 必须是正数2的n次方不可能≤0 // 2. a (a-1) 02的n次方二进制仅1个1减1后低位全1按位与结果为0 if (a 0 (a (a - 1)) 0) { printf(%d 是2的n次方\n, a); } else { printf(%d 不是2的n次方\n, a); } return 0; }如16 10000 01111 00000方法二#includestdio.h int main() { int a 0; int count 0; scanf(%d, a); while (a ! 1 a%20) { a (a 1) / 2; } printf(a 1 ? 是2的n次方\n : 不是2的n次方\n); return 0; }是 2 的 n 次方的数除2的余数不等于0且最后除2等于1练习4 一个数的某位二进制置0或者置1#include stdio.h int main() { int a 13; a a | (1 4); printf(a %d\n, a); a a ~(1 4); printf(a %d\n, a); return 0; }掩码Mask专门构造的一个数用来和目标数做位运算实现对指定位的操作。置 1 操作用num | (1 n)核心是按位或 掩码第 n 位为 1目标位强制为 1其他位不变。置 0 操作用num ~(1 n)核心是按位与 掩码第 n 位为 0目标位强制为 0其他位不变。练习5 单身狗问题在一个整型数组中只有一个数字出现一次其他数组都是成对出现的请找出那个只出现一次的数字。例如数组中有1 2 3 4 5 1 2 3 4只有5出现一次其他数字都出现2次找出5//单身问题 #include stdio.h int main() { int a [] {1,2,3,4,1,2,3}; int b 0; for (int i 0; i sizeof(a) / sizeof(a[0]); i) { b^a[i]; } printf(%d\n, b); }思路核心原理异或运算中相同数异或00异或任何数该数本身解题逻辑用 0 依次异或数组所有元素成对出现的数会相互抵消结果为 0最终剩下的就是唯一出现一次的数代码执行0 异或 1→异或 2→异或 3→异或 4→异或 1→异或 2→异或 3 (1^1)^(2^2)^(3^3)^4 0^0^0^4 4。练习6 打印整数二进制的奇数位和偶数位#includestdio.h int main() { int a 0; scanf(%d, a); int b a; //偶数位清0 for (int i 1; i 32; i 2) { b b ~(1 i); } for (int i 31;i 0; i--) { printf(%d, (b i) 1); } printf(\n); //奇数位清0 int c a; for (int j 0; j 32; j 2) { c c ~(1 j); } for (int j 31; j 0; j--) { printf(%d, (c j) 1); } printf(\n); return 0; }

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…