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

发布于:2025-05-08 ⋅ 阅读:(17) ⋅ 点赞:(0)

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)

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


网站公告

今日签到

点亮在社区的每一天
去签到