• C++
  • C++位运算符教程 通俗易懂 0基础 详细

  • @ 2025-3-13 21:47:15

1. 什么是位运算符

在 C++ 里,位运算符主要是对整数类型(像 intchar 等)的数据按二进制位进行操作。位运算能直接操作数据的二进制位,在某些场景下可显著提升程序的执行效率,还能用于实现特定的功能,例如数据加密、压缩以及底层硬件控制等。

2. 常见的位运算符

2.1 按位与运算符(&

  • 运算规则:对两个操作数的对应二进制位进行与运算,只有当两个对应位都为 1 时,结果位才为 1,否则为 0。
  • 示例代码
#include <iostream>
int main() {
    int a = 5;  // 二进制表示: 0101
    int b = 3;  // 二进制表示: 0011
    int result = a & b;  // 按位与运算
    std::cout << "5 & 3 的结果是: " << result << std::endl;
    return 0;
}
  • 代码解释
    • 5 的二进制是 0101,3 的二进制是 0011。
    • 按位与运算时,从右到左逐位比较:
      • 第 1 位:1 和 1 相与得 1。
      • 第 2 位:0 和 1 相与得 0。
      • 第 3 位:1 和 0 相与得 0。
      • 第 4 位:0 和 0 相与得 0。
    • 所以结果是 0001,即十进制的 1。

2.2 按位或运算符(|

  • 运算规则:对两个操作数的对应二进制位进行或运算,只要两个对应位中有一个为 1,结果位就为 1,只有当两个对应位都为 0 时,结果位才为 0。
  • 示例代码
#include <iostream>
int main() {
    int a = 5;  // 二进制表示: 0101
    int b = 3;  // 二进制表示: 0011
    int result = a | b;  // 按位或运算
    std::cout << "5 | 3 的结果是: " << result << std::endl;
    return 0;
}
  • 代码解释
    • 5 的二进制是 0101,3 的二进制是 0011。
    • 按位或运算时,从右到左逐位比较:
      • 第 1 位:1 和 1 相或得 1。
      • 第 2 位:0 和 1 相或得 1。
      • 第 3 位:1 和 0 相或得 1。
      • 第 4 位:0 和 0 相或得 0。
    • 所以结果是 0111,即十进制的 7。

2.3 按位异或运算符(^

  • 运算规则:对两个操作数的对应二进制位进行异或运算,当两个对应位不同时,结果位为 1,相同时结果位为 0。
  • 示例代码
#include <iostream>
int main() {
    int a = 5;  // 二进制表示: 0101
    int b = 3;  // 二进制表示: 0011
    int result = a ^ b;  // 按位异或运算
    std::cout << "5 ^ 3 的结果是: " << result << std::endl;
    return 0;
}
  • 代码解释
    • 5 的二进制是 0101,3 的二进制是 0011。
    • 按位异或运算时,从右到左逐位比较:
      • 第 1 位:1 和 1 异或得 0。
      • 第 2 位:0 和 1 异或得 1。
      • 第 3 位:1 和 0 异或得 1。
      • 第 4 位:0 和 0 异或得 0。
    • 所以结果是 0110,即十进制的 6。

2.4 按位取反运算符(~

  • 运算规则:对操作数的每个二进制位取反,即 1 变为 0,0 变为 1。
  • 示例代码
#include <iostream>
int main() {
    int a = 5;  // 二进制表示: 0101
    int result = ~a;  // 按位取反运算
    std::cout << "~5 的结果是: " << result << std::endl;
    return 0;
}
  • 代码解释
    • 5 的二进制是 0101。
    • 按位取反后得到 1010。
    • 在计算机中,整数通常以补码形式存储,这里得到的 1010 是补码形式。要得到其十进制值,先减 1 得到反码 1001,再取反得到原码 1110,即十进制的 -6。

2.5 左移运算符(<<

  • 运算规则:将操作数的二进制位向左移动指定的位数,右边空出的位用 0 填充。左移一位相当于将该数乘以 2。
  • 示例代码
#include <iostream>
int main() {
    int a = 5;  // 二进制表示: 0101
    int result = a << 2;  // 左移 2 位
    std::cout << "5 << 2 的结果是: " << result << std::endl;
    return 0;
}
  • 代码解释
    • 5 的二进制是 0101。
    • 左移 2 位后得到 010100,即十进制的 20。

2.6 右移运算符(>>

  • 运算规则:将操作数的二进制位向右移动指定的位数。对于无符号数,左边空出的位用 0 填充;对于有符号数,左边空出的位用符号位填充(正数用 0 填充,负数用 1 填充)。右移一位相当于将该数除以 2。
  • 示例代码
#include <iostream>
int main() {
    int a = 8;  // 二进制表示: 1000
    int result = a >> 2;  // 右移 2 位
    std::cout << "8 >> 2 的结果是: " << result << std::endl;
    return 0;
}
  • 代码解释
    • 8 的二进制是 1000。
    • 右移 2 位后得到 0010,即十进制的 2。

3. 位运算符的常见应用

3.1 判断奇偶性

可以使用按位与运算符 & 来判断一个数是奇数还是偶数。如果一个数的二进制表示的最低位是 1,则该数为奇数;如果最低位是 0,则该数为偶数。可以将该数与 1 进行按位与运算,如果结果为 1,则是奇数;结果为 0,则是偶数。

#include <iostream>
void checkEvenOdd(int num) {
    if (num & 1) {
        std::cout << num << " 是奇数。" << std::endl;
    } else {
        std::cout << num << " 是偶数。" << std::endl;
    }
}
int main() {
    int num = 7;
    checkEvenOdd(num);
    return 0;
}

3.2 交换两个数的值

可以使用按位异或运算符 ^ 来交换两个数的值,而无需使用额外的临时变量。

#include <iostream>
void swap(int& a, int& b) {
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}
int main() {
    int x = 5;
    int y = 3;
    std::cout << "交换前: x = " << x << ", y = " << y << std::endl;
    swap(x, y);
    std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
    return 0;
}

4. 总结

位运算符能直接操作数据的二进制位,在某些场景下可提高程序效率。在使用位运算符时,要清楚操作数的二进制表示以及运算符的运算规则。同时,要留意不同类型(有符号数和无符号数)在右移操作时的差异。通过不断练习,你能更熟练地运用位运算符解决实际问题。

1 条评论

  • @ 2025-3-13 21:48:48

    要更好地理解位运算符的优先级,可以从了解优先级顺序、借助表达式示例、结合代码实践以及与逻辑运算符对比等方面入手,下面为你详细介绍:

    1. 牢记位运算符优先级顺序

    在C++里,位运算符的优先级高低有所不同,你需要记住常见位运算符的优先级顺序。一般而言,按位取反运算符(~)优先级最高,接着是左移(<<)和右移(>>)运算符,然后是按位与(&)运算符,再之后是按位异或(^)运算符,最后是按位或(|)运算符。

    你可以参考下面这个完整的运算符优先级表格,位运算符相关部分已经加粗展示:

    2. 借助表达式示例理解

    可以通过一些具体的表达式示例来深入理解优先级。例如对于表达式 3 & 5 << 2,根据优先级,先进行左移操作 5 << 2,5 的二进制是 0101,左移 2 位后得到 010100 即 20,然后再进行按位与操作 3 & 20,3 的二进制是 0011,20 的二进制是 010100,按位与结果为 0,所以整个表达式结果是 0。

    3. 结合代码实践体会

    自己编写一些包含多种位运算符的代码,然后运行并观察结果,通过这种方式能更直观地感受优先级的影响。以下是一个示例代码:

    #include <iostream>
    
    int main() {
        int a = 3;
        int b = 5;
        int c = 2;
    
        // 按照优先级计算
        int result = a & b << c;
    
        std::cout << "表达式 a & b << c 的结果是: " << result << std::endl;
        return 0;
    }
    

    在这个代码中,你可以修改变量的值和运算符,运行程序观察不同情况下的计算结果,从而加深对优先级的理解。

    4. 与逻辑运算符对比

    把位运算符和逻辑运算符(如 &&||)放在一起对比,了解它们之间的优先级关系。逻辑运算符的优先级通常低于位运算符,例如在表达式 3 & 5 || 2 << 1 中,会先计算 3 & 52 << 1,再进行逻辑或运算。

    5. 使用括号明确运算顺序

    当表达式较为复杂,难以确定运算顺序时,可使用括号来明确优先级。例如 (3 & 5) << 2 就明确了先进行按位与操作,再进行左移操作。这样不仅能保证计算结果符合预期,还能提高代码的可读性。

    6. 分析错误案例

    尝试编写一些故意违背优先级规则的代码,运行后观察出现的错误结果,然后分析原因,这样能更深刻地理解优先级的重要性。例如,如果错误地认为按位与运算符优先级高于左移运算符,写出 3 & (5 << 2) 以外的错误表达式,运行后结果会和预期不符,此时分析错误就能强化对优先级的认识。

    • 1