当前位置: 首页 > news >正文

C++中的位运算符:与、或、异或详解

C++中的位运算符:与、或、异或详解

在C++编程中,位运算符是对整数类型的二进制位进行操作的运算符。它们直接操作数据的二进制表示,是底层编程和高性能计算中的重要工具。本文将详细介绍C++中的与(&)、或(|)、异或(^)等位运算符。

一、位运算符概述

C++提供了以下几种位运算符:

  1. 按位与(&)
  2. 按位或(|)
  3. 按位异或(^)
  4. 按位取反(~)
  5. 左移(<<)
  6. 右移(>>)

这些运算符只能用于整数类型(char, short, int, long, long long及其unsigned版本)。

二、各运算符详解

1. 按位与(&)

功能:对两个操作数的每一位执行逻辑与操作,只有两个对应位都为1时,结果位才为1。

真值表

A & B = Result
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

示例代码

int a = 5;      // 0101
int b = 3;      // 0011
int result = a & b; // 0001 (1)

常见用途

  • 掩码操作(提取特定位)
  • 判断奇偶(n & 1)
  • 清零特定位

2. 按位或(|)

功能:对两个操作数的每一位执行逻辑或操作,只要有一个对应位为1,结果位就为1。

真值表

A | B = Result
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

示例代码

int a = 5;      // 0101
int b = 3;      // 0011
int result = a | b; // 0111 (7)

常见用途

  • 设置特定位
  • 合并位掩码

3. 按位异或(^)

功能:对两个操作数的每一位执行异或操作,当对应位不同时结果为1,相同时为0。

真值表

A ^ B = Result
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

示例代码

int a = 5;      // 0101
int b = 3;      // 0011
int result = a ^ b; // 0110 (6)

常见用途

  • 交换两个变量的值(不使用临时变量)
  • 加密/解密
  • 找出只出现一次的数字(其他数字都出现两次)

4. 按位取反(~)

功能:对操作数的每一位执行逻辑非操作,即0变1,1变0。

示例代码

int a = 5;      // 0101
int result = ~a; // 1010 (取决于整数位数,实际是补码表示)

注意:结果取决于整数类型和位数,因为高位也会被取反。

5. 位移运算符(<< 和 >>)

虽然这不是本文重点,但简单介绍:

  • 左移(<<):将二进制位向左移动指定位数,低位补0
  • 右移(>>):将二进制位向右移动指定位数,对于无符号数高位补0,有符号数取决于实现

三、实用技巧示例

1. 交换两个变量的值(不使用临时变量)

int a = 5, b = 3;
a ^= b;
b ^= a;
a ^= b;
// 现在a=3, b=5

2. 判断奇偶

bool isOdd = num & 1; // 如果为true则是奇数

3. 检查第n位是否设置

bool isSet = num & (1 << n); // 检查第n位(从0开始)

4. 设置第n位

num |= (1 << n); // 设置第n位为1

5. 清除第n位

num &= ~(1 << n); // 清除第n位(设为0)

6. 切换第n位

num ^= (1 << n); // 如果第n位是1则变为0,反之亦然

四、注意事项

  1. 运算符优先级:位运算符的优先级通常低于算术运算符,高于逻辑运算符。建议使用括号明确运算顺序。

  2. 符号位处理:对有符号数进行位操作时要小心符号位的处理,可能产生意外结果。

  3. 移位运算陷阱

    • 不要对负数进行左移(行为未定义)
    • 不要移位超过或等于操作数的位数(行为未定义)
  4. 可读性:虽然位运算效率高,但可能降低代码可读性,建议添加适当注释。

五、性能考虑

位运算通常在CPU级别非常高效,因为它们直接映射到处理器指令。在性能敏感的代码中(如图形处理、加密算法、网络协议等),合理使用位运算可以显著提高性能。

六、总结

C++的位运算符提供了直接操作二进制位的能力,是底层编程和高性能计算的重要工具。理解并掌握这些运算符对于编写高效、紧凑的代码非常有帮助。然而,使用时也需要注意其陷阱和限制,特别是在处理有符号数和移位操作时。

希望本文能帮助你更好地理解和应用C++中的位运算符。如果你有任何问题或补充,欢迎在评论区留言讨论!


拓展

找出数组中只出现一次的数字

题目描述

给定一个整数数组 nums,其中恰好有一个元素只出现一次,其余每个元素均出现两次。找出那个只出现一次的元素。

示例 1:

输入: [2,2,1]
输出: 1

示例 2:

输入: [4,1,2,1,2]
输出: 4

数据范围

  • 数组长度 n 满足:0 < n ≤ 4000
  • 数组中的每个元素值 val 满足:0 ≤ val ≤ 4000

进阶要求

  • 空间复杂度:O(1)
  • 时间复杂度:O(n)

解题思路

方法一:哈希表法(不满足空间复杂度要求)

思路:
使用哈希表记录每个数字出现的次数,最后遍历哈希表找出只出现一次的数字。

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)(不满足进阶要求)

代码示例:

#include <unordered_map>
#include <vector>int singleNumber(std::vector<int>& nums) {std::unordered_map<int, int> count;for (int num : nums) {count[num]++;}for (auto& pair : count) {if (pair.second == 1) {return pair.first;}}return -1; // 根据题目描述,不会执行到这里
}

方法二:数学法(不满足空间复杂度要求)

思路:
利用集合去重,计算 2 * (a + b + c) - (a + a + b + b + c) = c

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)(不满足进阶要求)

代码示例:

#include <unordered_set>
#include <vector>
#include <numeric>int singleNumber(std::vector<int>& nums) {std::unordered_set<int> set(nums.begin(), nums.end());int sum_set = std::accumulate(set.begin(), set.end(), 0);int sum_nums = std::accumulate(nums.begin(), nums.end(), 0);return 2 * sum_set - sum_nums;
}

方法三:位运算(最优解)

思路:
利用异或运算的性质:

  1. 任何数和0异或都是它本身:a ^ 0 = a
  2. 任何数和自身异或都是0:a ^ a = 0
  3. 异或运算满足交换律和结合律

将所有数字进行异或运算,出现两次的数字会互相抵消,最终结果就是只出现一次的数字。

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)(满足进阶要求)

代码示例:

#include <vector>int singleNumber(std::vector<int>& nums) {int result = 0;for (int num : nums) {result ^= num;}return result;
}

位运算解法详细说明

为什么异或运算能解决这个问题?

异或运算有以下重要性质:

  1. 交换律a ^ b = b ^ a
  2. 结合律a ^ (b ^ c) = (a ^ b) ^ c
  3. 自反性a ^ a = 0
  4. 恒等性a ^ 0 = a

因此,对于数组 [a, b, a, c, b],计算过程为:

a ^ b ^ a ^ c ^ b
= (a ^ a) ^ (b ^ b) ^ c
= 0 ^ 0 ^ c
= c

边界情况处理

虽然题目保证有解,但实际编程中可以考虑以下边界情况:

  1. 空数组(根据题目描述 0 < n,可以忽略)
  2. 所有元素都出现两次(题目保证有一个只出现一次的元素)

其他可能的解法

方法四:排序法(不满足时间复杂度要求)

思路:
先对数组排序,然后遍历查找只出现一次的元素。

复杂度分析:

  • 时间复杂度:O(n log n)(不满足进阶要求)
  • 空间复杂度:O(1)(原地排序)

代码示例:

#include <vector>
#include <algorithm>int singleNumber(std::vector<int>& nums) {std::sort(nums.begin(), nums.end());for (int i = 0; i < nums.size() - 1; i += 2) {if (nums[i] != nums[i + 1]) {return nums[i];}}return nums.back();
}

总结

方法时间复杂度空间复杂度是否满足要求
哈希表法O(n)O(n)
数学法O(n)O(n)
位运算法O(n)O(1)
排序法O(n log n)O(1)

最优解:位运算方法,满足所有要求且实现简洁高效。

相关文章:

  • 阿里云2核2g安装nexus
  • shell脚本--2
  • 如何在大型项目中解决 VsCode 语言服务器崩溃的问题
  • 【shardingsphere分布式主键无效】
  • Kubernetes(k8s)学习笔记(八)--KubeSphere定制化安装
  • C 语言编码规范
  • Selenium使用指南
  • 21. LangChain金融领域:合同审查与风险预警自动化
  • 802.11s Mesh 组网框架流程
  • 排序算法——桶排序
  • 一个电平转换电路导致MCU/FPGA通讯波形失真的原因分析
  • 阿里云codeup以及本地gitclone+http
  • AB测试面试题
  • 年化50.1%,回撤23%|从数据加载,因子分析到lightGBM因子合成,智能策略开发全流程(附python代码下载)
  • livedata使用,完整的livedata的Demo
  • 缓存菜品-04.功能测试
  • IEC103 转 ModbusTCP 网关
  • MapReduce架构-打包运行
  • 【计算机架构】RISC(精简指令集计算机)架构
  • powershell批处理——io校验
  • 母亲节|写给妈妈
  • 拿出压箱底作品,北京交响乐团让上海观众享受音乐盛宴
  • 逆境之上,万物生长
  • 著名国际关系理论家、“软实力”概念提出者约瑟夫•奈逝世
  • AI智能体,是不是可以慢一点? | ToB产业观察
  • 退休11年后,71岁四川厅官杨家卷被查