先看如下一段左移右移的代码及其结果:
代码
#include
"
stdio.h
"
char
leftshift(
char
i,
int
n) {
if
(n
<
0
)
return
-
1
;
return
i
<<
n; }
char
rightshift(
char
i,
int
n) {
if
(n
<
0
)
return
-
1
;
return
i
>>
n; }
int
main() {
//
leftshift
char
a1
=
127
;
char
a2
=
-
1
;
for
(
int
i
=
1
; i
<=
8
; i
++
) printf(
"
%d<<%d = %d;\n
"
, a1, i, leftshift(a1,i));
for
(i
=
1
; i
<=
8
; i
++
) printf(
"
%d<<%d = %d;\n
"
, a2, i, leftshift(a2,i));
//
rightshift
a1
=
127
; a2
=
-
128
;
for
(i
=
1
; i
<=
8
; i
++
) printf(
"
%d>>%d = %d;\n
"
, a1, i, rightshift(a1,i));
for
(i
=
1
; i
<=
8
; i
++
) printf(
"
%d>>%d = %d;\n
"
, a2, i, rightshift(a2,i));
return
0
; }
结果为:
结果
127
<<
1
=
-
2
;
127
<<
2
=
-
4
;
127
<<
3
=
-
8
;
127
<<
4
=
-
16
;
127
<<
5
=
-
32
;
127
<<
6
=
-
64
;
127
<<
7
=
-
128
;
127
<<
8
=
0
;
-
1
<<
1
=
-
2
;
-
1
<<
2
=
-
4
;
-
1
<<
3
=
-
8
;
-
1
<<
4
=
-
16
;
-
1
<<
5
=
-
32
;
-
1
<<
6
=
-
64
;
-
1
<<
7
=
-
128
;
-
1
<<
8
=
0
;
127
>>
1
=
63
;
127
>>
2
=
31
;
127
>>
3
=
15
;
127
>>
4
=
7
;
127
>>
5
=
3
;
127
>>
6
=
1
;
127
>>
7
=
0
;
127
>>
8
=
0
;
-
128
>>
1
=
-
64
;
-
128
>>
2
=
-
32
;
-
128
>>
3
=
-
16
;
-
128
>>
4
=
-
8
;
-
128
>>
5
=
-
4
;
-
128
>>
6
=
-
2
;
-
128
>>
7
=
-
1
;
-
128
>>
8
=
-
1
;
左移操作(<<)
规则:
右边空出的位用0填补
高位左移溢出则舍弃该高位。
计算机中常用补码表示数据:
数据 127,补码和原码一样:0111 1111。
左移一位: 1111 1110 -> 这个补码对应的原码为:1000 0010 对应十进制:-2
左移二位: 1111 1100 -> 这个补码对应的原码为:1000 0100 对应十进制:-4
左移三位: 1111 1000 -> 这个补码对应的原码为:1000 1000 对应十进制:-8
左移四位: 1111 0000 -> 这个补码对应的原码为:1001 0000 对应十进制:-16
左移五位: 1110 0000 -> 这个补码对应的原码为:1010 0000 对应十进制:-32
左移六位: 1100 0000 -> 这个补码对应的原码为:1100 0000 对应十进制:-64
左移七位: 1000 0000 -> 这个补码对应的原码为:1000 0000 对应十进制:-128
左移八位: 0000 0000 -> 这个补码对应的原码为:0000 0000 对应十进制:0
注:
原码到补码的计算方式:取反+1,
补码到原码的计算方式:-1再取反。
数据-1,它的原码为1000 0001,补码为1111 1111
左移一位: 1111 1110 -> 这个补码对应的原码为:1000 0010 对应十进制:-2
左移二位: 1111 1100 -> 这个补码对应的原码为:1000 0100 对应十进制:-4
左移三位: 1111 1000 -> 这个补码对应的原码为:1000 1000 对应十进制:-8
左移四位: 1111 0000 -> 这个补码对应的原码为:1001 0000 对应十进制:-16
左移五位: 1110 0000 -> 这个补码对应的原码为:1010 0000 对应十进制:-32
左移六位: 1100 0000 -> 这个补码对应的原码为:1100 0000 对应十进制:-64
左移七位: 1000 0000 -> 这个补码对应的原码为:1000 0000 对应十进制:-128
左移八位: 0000 0000 -> 这个补码对应的原码为:0000 0000 对应十进制:0
可以看出127和-1的结果完全一样。移位操作与正负数无关,它只是忠实的将所有位进行移动,补0,舍弃操作。
右移操作(>>)
规则:
左边空出的位用0或者1填补。正数用0填补,负数用1填补。注:不同的环境填补方式可能不同;
低位右移溢出则舍弃该位。
1、127的补码:0111 1111
右移一位: 0011 1111 -> 原码同补码一样 对应十进制:63
右移二位: 0001 1111 -> 原码同补码一样 对应十进制:31
右移三位: 0000 1111 -> 原码同补码一样 对应十进制:15
右移四位: 0000 0111 -> 原码同补码一样 对应十进制:7
右移五位: 0000 0011 -> 原码同补码一样 对应十进制:3
右移六位: 0000 0001 -> 原码同补码一样 对应十进制:1
右移七位: 0000 0000 -> 原码同补码一样 对应十进制:0
右移八位: 0000 0000 -> 原码同补码一样 对应十进制:0
2、-128的补码:1000 0000
右移一位: 1100 0000 -> 这个补码对应的原码为:1100 0000 对应十进制:-64
右移二位: 1110 0000 -> 这个补码对应的原码为:1010 0000 对应十进制:-32
右移三位: 1111 0000 -> 这个补码对应的原码为:1001 0000 对应十进制:-16
右移四位: 1111 1000 -> 这个补码对应的原码为:1000 1000 对应十进制:-8
右移五位: 1111 1100 -> 这个补码对应的原码为:1000 0100 对应十进制:-4
右移六位: 1111 1110 -> 这个补码对应的原码为:1000 0010 对应十进制:-2
右移七位: 1111 1111 -> 这个补码对应的原码为:1000 0001 对应十进制:-1
右移八位: 1111 1111 -> 这个补码对应的原码为:1000 0001 对应十进制:-1
常见应用
左移相当于*2,只是要注意边界问题。如char a = 65; a<<1 按照*2来算为130;但有符号char的取值范围-128~127,已经越界,多超出了3个数值,所以从-128算起的第三个数值-126才是a<<1的正确结果。
而右移相当于除以2,只是要注意移位比较多的时候结果会趋近去一个非常小的数,如上面结果中的-1,0。
其它的四种位运算:
与运算(&)
1、与0相与可清零
2、与1相与可保留原值
或运算(|)
1、与0相或可保留原值
2、与1相与可齐设1
异或运算(^)
1、与0异或保留原值
2、与1异或比特值反转
3、可通过某种算法,使用异或实现交换两个值
异或运算是有结合律的
取反(~)