关于位运算

一直没怎么了解过位运算符,学习C的时候只粗略的看了下位移 >>有符号右移,<<左移,今天看面试题目的发现,有的题目提到了位运算符,没错当时是懵逼的。位运算符分别是:>> (右移运算符),<< (左移运算符),>>> (无符号右移运算符),& (按位与),| (按位或),~ (按位非)。其中(~)是一元操作符,其他的都是二元操作符。😕 :smiley:

>> (右移运算符)

例如 5 >> 2

//language C++
#include <iostream>

int main() {
    std::cout << (5>>2) << std::endl;  // 结果为1
    return 0;
}

首先会5转为二进制,默认为int型,即32位。

5
0000 0000 0000 0000 0000 0000 0000 0101
1 // 结果
0000 0000 0000 0000 0000 0000 0000 0001

例如 -5 >> 2

//language C++
#include <iostream>

int main() {
    std::cout << (-5>>2) << std::endl;  // 结果为-2
    return 0;
}

结果

-5
1111 1111 1111 1111 1111 1111 1111 1011
-2 // 结果
1111 1111 1111 1111 1111 1111 1111 1110

例如 -5 >> -2

//language C++
#include <iostream>

int main() {
    std::cout << (-5>>-2) << std::endl;  // 结果为-1
    return 0;
}

结果

-5
1111 1111 1111 1111 1111 1111 1111 1011
-1 //结果
1111 1111 1111 1111 1111 1111 1111 1111

5往右位移两位,高位用0补(正数用0补位,负数用1补位)

C++输出二进制

#include <iostream>
#include <bitset>
int main() {
    std::cout << std::bitset<32>(-5) << std::endl; // 输出-5的二进制
    return 0;
}

<< (左移运算符)

例如 5 << 2

//language C++
#include <iostream>

int main() {
    std::cout << (5<<2) << std::endl;  // 结果为20
    return 0;
}

结果

5
0000 0000 0000 0000 0000 0000 0000 0101
20 //结果
0000 0000 0000 0000 0000 0000 0001 0100

5往左移了两位,使用了0补位

例如 -5 << 2

//language C++
#include <iostream>

int main() {
    std::cout << (-5<<2) << std::endl;  // 结果为-20
    return 0;
}

结果

-5
1111 1111 1111 1111 1111 1111 1111 1011
-20 //结果
1111 1111 1111 1111 1111 1111 1110 1100

-5往左移了两位

例如 5 << -2

//language C++
#include <iostream>

int main() {
    std::cout << (5<<-2) << std::endl;  // 结果为0
    return 0;
}

结果

5
0000 0000 0000 0000 0000 0000 0000 0101
0
0000 0000 0000 0000 0000 0000 0000 0000

经过有趣的实验发现,只要左移位数为负数,无论被左移数为负数还是正数,结果都为0

>>> (无符号右移运算符)

例如 5 >> 2

//language C++
#include <iostream>

int main() {
    int nOrg = 5;
    unsigned int nuNum = (unsigned)nOrg; // 转化为无符号数
    std::cout << (nuNum >> 2) << std::endl; // 无符号右移,结果为1
    return 0;
}

结果

5 //无符号表示非负数
0000 0000 0000 0000 0000 0000 0000 0101
1 // 结果
0000 0000 0000 0000 0000 0000 0000 0001

其实5默认就是无符号数,无符号数表示非负数(0及正数)。结果和 5 >> 2相同

例如 -5 >> 2

//language C++
#include <iostream>

int main() {
    int nOrg = -5;
    unsigned int nuNum = (unsigned)nOrg; // 转化为无符号数
    std::cout << (nuNum >> 2) << std::endl; // 无符号右移,结果为1073741822
    return 0;
}

结果

-5
1111 1111 1111 1111 1111 1111 1111 1011
1073741822 //结果
0011 1111 1111 1111 1111 1111 1111 1110

发现使用 >>>(无符号右移运算符) 负数高位不再使用1补位,而是使用0补位

例如 -5 >>> -2

int main() {
    int nOrg = -5;
    unsigned int nuNum = (unsigned)nOrg; // 转化为无符号数
    std::cout << (nuNum >> -2) << std::endl; // 无符号右移,结果为3
    return 0;
}

结果

-5
1111 1111 1111 1111 1111 1111 1111 1011
3
0000 0000 0000 0000 0000 0000 0000 0011

嗯....好像并没有发现什么,再接着做实验
-5 >>> -3 = 7

1111 1111 1111 1111 1111 1111 1111 1011
7
0000 0000 0000 0000 0000 0000 0000 0111

-5 >>> -4 = 15

1111 1111 1111 1111 1111 1111 1111 1011
15
0000 0000 0000 0000 0000 0000 0000 1111

也没什么变化,唯一变的就是二进制1越来越多,试试更大的数
-5 >>> -32 = -5

1111 1111 1111 1111 1111 1111 1111 1011
-5
1111 1111 1111 1111 1111 1111 1111 1011

好像发现问题了,再试一下
-5 >>> -31 = 2147483645

1111 1111 1111 1111 1111 1111 1111 1011
2147483645
0111 1111 1111 1111 1111 1111 1111 1101

-5 >>> -31 = 1073741822

1111 1111 1111 1111 1111 1111 1111 1011
1073741822
0011 1111 1111 1111 1111 1111 1111 1110

这么多实验,可以得出结论,如图
1344887797.png

& (按位与)

例如 5 & 2

int main() {
    std::cout << (5 & 2) << std::endl; //结果为0
    return 0;
}

二进制

5
0000 0000 0000 0000 0000 0000 0000 0101
2
0000 0000 0000 0000 0000 0000 0000 0010
0 // 结果
0000 0000 0000 0000 0000 0000 0000 0000

例如 5 & 3

二进制

5
0000 0000 0000 0000 0000 0000 0000 0101
3
0000 0000 0000 0000 0000 0000 0000 0011
1 // 结果
0000 0000 0000 0000 0000 0000 0000 0001

例如 5 & -2

二进制

5
0000 0000 0000 0000 0000 0000 0000 0101
-2
1111 1111 1111 1111 1111 1111 1111 1110
4
0000 0000 0000 0000 0000 0000 0000 0100

| (按位或)

例如 5 | 2

int main() {
    std::cout << (5 | 2) << std::endl; //结果为7
    return 0;
}

二进制

5
0000 0000 0000 0000 0000 0000 0000 0101
2
0000 0000 0000 0000 0000 0000 0000 0010
7 //结果
0000 0000 0000 0000 0000 0000 0000 0111

例如 5 | -2

二进制,结果为一

5
0000 0000 0000 0000 0000 0000 0000 0101
-2
1111 1111 1111 1111 1111 1111 1111 1110
-1
1111 1111 1111 1111 1111 1111 1111 1111

~ (按位非)

例如 ~5

5
0000 0000 0000 0000 0000 0000 0000 0101
-6
1111 1111 1111 1111 1111 1111 1111 1010