C++位运算

一、基础

1.1 概述

位运算是一种在计算机编程中直接操作二进制位的操作方式。它可以高效地处理数据,并且在某些场景下比普通的算术运算更快。位运算通常用于低级编程、硬件编程、性能优化和加密算法等领域。
优势:1.速度快;2.节省寄存器/内存空间

1.2 原码、反码、补码

1.2.1 原码

原码是一种简单的表示方法,使用最高位(符号位)表示正负号,剩余位表示数值的绝对值。
符号位:0表示正数,1表示负数。
数值位:表示数值的绝对值。

1.2.2 反码

反码通过将原码的数值位按位取反得到,符号位保持不变。
符号位:与原码相同。
数值位:正数的反码与原码相同,负数的反码是将原码的数值位按位取反。

1.2.3 补码

补码是现代计算机系统中最常用的有符号整数表示方法。它通过将反码加1得到。
符号位:与原码相同。
数值位:正数的补码与原码相同,负数的补码是将原码的数值位按位取反再加1。

1.2.4 示例对比

十进制原码反码补码
+5000001010000010100000101
-5100001011111101011111011

1.3 基本运算符

1.3.1 按位与(&)

对每个二进制位执行逻辑与操作。
对应位置都为1,结果才为1,否则为0。
示例:13 & 11 = 9

十进制二进制
1300001101
1100001011
900001001

运算律:

  • 交换律:A & B = B & A
  • 结合律:A & (B & C) = (A & B) & C
  • 恒等律:A & 0 = 0
  • 同一律:A & A = A
  • 消去律:A & ~A = 0
  • 分配律:A & (B | C) = (A & B) | (A & C)

1.3.2 按位或(|)

对每个二进制位执行逻辑或操作。
对应位置只要有一个为1,结果就为1。
示例:13 | 11 = 15

十进制二进制
1300001101
1100001011
1500001111

运算律:

  • 交换律:A | B = B | A
  • 结合律:A | (B | C) = (A | B) | C
  • 恒等律:A | 0 = A
  • 同一律:A | A = A
  • 消去律:A | ~A = 1
  • 分配律:A | (B & C) = (A | B) & (A | C)

1.3.3 按位异或(^)

对每个二进制位执行逻辑异或操作。
对应位置不同,结果为1,相同为0。
示例:13 ^ 11 = 6

十进制二进制
1300001101
1100001011
600000110

运算律:

  • 交换律:A ^ B = B ^ A
  • 结合律:A ^ (B ^ C) = (A ^ B) ^ C
  • 恒等律:A ^ 0 = A
  • 自反律:A ^ A = 0
  • 消去律:A ^ ~A = 1

1.3.4 按位取反(~)

对每个二进制位执行逻辑取反操作。
对每一个位进行取反操作。
用途:按位取反。
运算律:

  • 双重否定律:~~A = A
  • 德摩根定律:~(A & B) = ~A | ~B
          ~(A | B) = ~A & ~B

1.3.5 左移(<<)

将二进制位左移指定的位数。
将一个数(有、无符号数)的二进制位左移指定的位数,右边补0。
注意,对于int类型的变量,左移超过31位(假设int为32位)会导致未定义行为。

1.3.6 右移(>>)

将二进制位右移指定的位数。
将一个数的二进制位右移指定的位数。
算术右移(对于有符号数):用符号位填充左侧。
逻辑右移(对于无符号数):用0填充左侧。
注意,在某些编译器中,右移负数可能会保留符号位,这取决于具体的实现(算术右移和逻辑右移)。
尽量不要使用带符号的整数参加位运算!

1.3.7优先级(从上到下,由高到低)

运算符功能
+ +递增
- -递减
~取反
-一元负号
+一元正号
*解引用
&取地址
*乘法
/除法
%取模(取余)
+加法
-减法
<<向左移位
>>向右移位
<小于
<=小于等于
>大于
>=大于等于
==相等
!=不相等
&位与
^位异或
|位或
&&逻辑与
||逻辑或
? :条件
=赋值

完整版见《C++ Primer》

二、常用技巧

位运算功能 示例
&清零末尾连续的1n & n + 1
清零最右边的1(判断是否是2的幂)n & n - 1
仅保留最右边的1n & -n
判断奇偶n & 1
|置1最右边的0n | n + 1
置1末尾连续的0n | n - 1
^交换两个数而无需临时变量a = a ^ b;
b = a ^ b;
a = a ^ b;
~取相反数~n + 1
<<快速乘以2的k次幂(代替对应乘法)n << k
>>快速除以2的k次幂(代替对应除法)n >> k
复合将第k+1位设为0n & ~(1 << k)
将第k+1位设为1n | 1 << k
翻转第k+1位n ^ 1 << k
取第k+1位n >> k & 1
将末尾k位设为0n & ~((1 << k) - 1)
将末尾k位设为1n | (1 << k) - 1
翻转末尾k位n ^ (1 << k) - 1
取末尾k位n & (1 << k) - 1
取绝对值mask = x >> 31;
x = (x + mask) ^ mask;

三、面试题型

3.1 位1的个数

原理:等同于清除最右边的1的次数

int count = 0;
while(n)
{	
	n = n & n - 1;
	count++;
}
return count;

扩展:

3.2 只出现一次的数字

原理:异或的恒等律和结合律

int num = 0;
for (int i = 0; i < length;i++ ) {
	num ^= arr[i];
}
return num;

扩展:

int ones = 0, twos = 0;
for (int num : nums) {
	//ones和twos分别记录出现一次和两次的数字
	tie(ones, twos) = pair{ones ^ num, twos | (ones & num)};
	//清除出现三次的数字
	int common_bit_mask = ~(ones & twos);
    ones &= common_bit_mask;
	twos &= common_bit_mask;
}
return ones;

3.3 位运算实现加减乘除

原理:

  • a + b:和为a ^ b,进位为(a & b ) << 1
  • a - b == a + (-b) == a + (~b + 1)
  • a * b:重复加法结合移位
  • a / b: 重复减法结合移位
#include <stdexcept>
int add(int a, int b) {
    while (b != 0) {
    	//对应位都为1则会产生进位
        int carry = (unsigned int)(a & b) << 1;
        //无进位时加法相当于异或
        a = a ^ b;
        //无进位加法与进位相机
        b = carry;
    }
    return a;
}

int subtract(int a, int b) {
    return add(a, add(~b, 1));
}

int multiply(int a, int b) {
    int result = 0;
    //二进制竖式乘法
    while (b != 0) {
        if (b & 1) {
            result = add(result, a);
        }
        a <<= 1;
        b >>= 1;
    }
    return result;
}

int divide(int dividend, int divisor) {
    if (divisor == 0) {
        throw std::overflow_error("Division by zero");
    }

    bool isNegative = (dividend < 0) ^ (divisor < 0);
    unsigned int a = abs(dividend);
    unsigned int b = abs(divisor);
    unsigned int result = 0;

	//二进制竖式除法
    for (int i = 31; i >= 0; i--) {
        if ((a >> i) >= b) {
            a -= b << i;
            result += 1 << i;
        }
    }
    //限制范围
    if(!isNegative && ans > INT_MAX)
            ans--;

    return isNegative ? -result : result;
}

PS:本文为学习总结,方便个人复习

参考

[1]C++位运算常见应用
[2]C++面试八股文之位运算问题详解
[3]【位运算】常用方法 & 技巧
[4]位运算 学习笔记【C++ 算法竞赛】
[5]C++:常用的位运算技巧
[6]C++:位操作基础篇之位操作全面总结
[7]力扣刷题c++位运算总结
[8]C++ Primer

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值