C++ 按位运算:二进制世界的“微观操控术”

按位运算是直接操作数据二进制位的底层运算,在C++中提供了对整数类型进行位级操作的强大能力。掌握它,你就能写出更高效、更巧妙的代码。

一、六大按位运算符

1. 按位与 &

对应位都为1时结果为1,否则为0。

int a = 5;    // 0101
int b = 3;    // 0011
int c = a & b; // 0001,即1

用途:清零特定位、提取特定位、判断奇偶(n & 1)。

2. 按位或 |

对应位有一个为1结果就为1。

int a = 5;    // 0101
int b = 3;    // 0011
int c = a | b; // 0111,即7

用途:设置特定位为1。

3. 按位异或 ^

对应位不同时为1,相同时为0。

int a = 5;    // 0101
int b = 3;    // 0011
int c = a ^ b; // 0110,即6

特性a ^ a = 0a ^ 0 = a。用于交换两数、查找唯一出现一次的数字。

4. 按位取反 ~

0变1,1变0。

unsigned char a = 0x0F; // 00001111
unsigned char b = ~a;   // 11110000,即0xF0

5. 左移 <<

二进制位向左移动,低位补0。

int a = 5;      // 0101
int b = a << 2; // 010100,即20

相当于乘以2ⁿ(不溢出时)。

6. 右移 >>

二进制位向右移动,无符号数高位补0,有符号数通常补符号位。

unsigned int a = 20;    // 10100
unsigned int b = a >> 2; // 00101,即5

相当于除以2ⁿ(向下取整)。

二、常见应用场景

1. 标志位管理

const int FLAG_A = 1 << 0; // 0001
const int FLAG_B = 1 << 1; // 0010
const int FLAG_C = 1 << 2; // 0100

int flags = FLAG_A | FLAG_B; // 设置A和B标志
if (flags & FLAG_B) {        // 检查B标志
    // B已设置
}
flags &= ~FLAG_A;            // 清除A标志

2. 高效算法

判断是否为2的幂

bool isPowerOfTwo(unsigned int n) {
    return n != 0 && (n & (n - 1)) == 0;
}

统计二进制中1的个数

int countOnes(unsigned int n) {
    int count = 0;
    while (n) {
        n &= (n - 1); // 清除最低位的1
        count++;
    }
    return count;
}

交换两个数(不使用临时变量)

void swap(int& a, int& b) {
    a ^= b;
    b ^= a;
    a ^= b;
}

三、⚠️ 注意事项与陷阱

1. 运算符优先级陷阱

这是最容易犯的错误!

// ❌ 错误!== 的优先级高于 &
if (x & y == 0) {  // 实际执行: x & (y == 0)
    // 不是检查x和y没有公共位!
}

// ✅ 正确!必须加括号
if ((x & y) == 0) {  // 正确检查x和y没有公共位
    // 业务逻辑
}

比较运算符(==, !=, <, >等)的优先级高于位运算符(&, |, ^),但低于移位运算符(<<, >>)。

黄金法则:混合使用位运算和其他运算符时,务必使用括号明确优先级

2. 有符号数的未定义行为

int a = 1 << 31;  // 32位int:未定义行为!
int b = -1 >> 1;  // 右移负数:实现定义,不同编译器结果可能不同

// 建议使用无符号类型进行位运算
unsigned int safe_a = 1U << 31;  // 明确的行为

3. 移位越界

unsigned int x = 1;
unsigned int y = x << 32;  // 未定义行为(移位位数≥类型位数)

// 安全做法
template<typename T>
T safe_shift(T value, unsigned shift) {
    if (shift >= sizeof(T) * CHAR_BIT) return 0;
    return value << shift;
}

四、C++20的增强

C++20引入了<bit>头文件,提供了跨平台的位操作:

#include <bit>
#include <cstdint>

uint32_t x = 0x12345678;

std::popcount(x);     // 统计1的个数
std::has_single_bit(x); // 是否只有一个1
std::bit_width(x);    // 表示x所需的最小位数
std::byteswap(x);     // 字节序转换(C++23)

五、实用技巧

1. 提取/设置特定位

// 提取第3位(从0开始)
bool bit3 = (value >> 3) & 1;

// 设置第5位为1
value |= (1 << 5);

// 设置第5位为0
value &= ~(1 << 5);

// 切换第5位
value ^= (1 << 5);

2. 快速乘除2的幂

unsigned int a = 42;
unsigned int b = a << 3;  // 乘以8
unsigned int c = a >> 2;  // 除以4

3. 位集合实现

class Bitset32 {
private:
    uint32_t data = 0;
public:
    void set(size_t pos)   { data |= (1U << pos); }
    void clear(size_t pos) { data &= ~(1U << pos); }
    bool test(size_t pos)  { return (data >> pos) & 1U; }
    void toggle(size_t pos){ data ^= (1U << pos); }
};

总结

按位运算让我们能够以最直接的方式操作数据,在性能关键场景下非常有用。记住几个关键点:

  1. 优先级陷阱:位运算符优先级低于比较运算符,务必加括号
  2. 类型安全:尽量用无符号类型进行位运算,避免未定义行为
  3. 边界检查:移位操作要确保不越界
  4. 代码可读性:复杂位运算要适当注释

虽然现代编译器优化能力很强,但在需要极致性能或进行底层编程时,位运算仍然是不可或缺的工具。掌握它,你就能写出更高效、更优雅的代码。

(注:示例代码中省略了必要的边界检查,实际使用时应根据需求添加。)

暂无评论

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇