C/C++语言中的位运算

数据在计算机中都是二进制存储的,都可进行按位运算,至于使用的何种编码规则,运算后的结果就不同了。在微机中存储数据有定点表示法和浮点表示法两种。定点表示法是将小数点固定在某一位置,通常有纯小数和纯整数两种方法。我们平时处理的整数就是纯整数这种表示,比于int,char,long类型等。浮点表示法主要用来表示实数,在C/C++中用来表示float和double型。本文主要针对纯整数的位运算,对于计算机、电子和控制等与计算机硬件相关专业的同学是必须掌握的。

1. C/C++语言中的运算符

C/C++语言中的运算符有如下几个:

<< >> ~ | ^ &

(1) 优先级高于四则运算

按位求反运算符 ~

(2) 优先级高于关系运算

左移运算符 <<

右移运算符 >>

(3) 优先级高于逻辑运算

按位或运算符 |

按位异或运算符 ^

按位与运算符 &

2. 短整型数的表示

位运算牵涉到数据的编码问题,计算机中所有数据都是二进制编码表示,对于带符号数在微机中通常有原码、反码和补码表示,表示的数的范围与数据使用的位数有关。设表示一个数的二进制的位数为n,通常为8,16,32,64等,在C/C++中整数为 字符型char 型8位,短整型short int 为16位,整型int和长整型long为32位。如果没有特别说明即未加关键字unsigned时均为带符号数。下面以短整型带符号数来说明原码、反码和补码的表示,其余的表示可以类推得到,为了后面举例方便,对无符号数也进行了解释。

(1) 原码表示法

原码表示法中最高位(第n-1位)为符号位,正数为0,负数为1,其余n-2…0位为二进制真值,如表1所示。  

表1 带符号的短整型数原码表示

十进制数

原码表示

+0

00000000 00000000

+1

00000000 00000001

+2

00000000 00000010

+32767

01111111 11111111

-0

10000000 00000000

-1

10000000 00000001

-32766

11111111 11111110

-32767

11111111 11111111

从表1可以看出,带符号的短整型原码表示法,共表示了65535个数,其中0有两个,有+0和-0。

如+25与-25的表示如下:

十进制数

原码表示

+25

00000000 00011001

-25

10000000 00011001

也可以表示成十六进制数0x0019和0x8019。

原码表示法简单并易于理解,与真值之间的转换也很方便,这是它的优点,缺点是进行加减运算时比较麻烦,加减运算时要考虑符号,比如25+(-26)=-1,如何才能得到呢?

+25 00000000 00011001

-26 10000000 00011010

01111111 11111111

是错误的,所以两个数相加且异号时必须比较两个数的绝对值大小才能确定实际的被减数,上例中-26为被减数,+25为减数,结果才能正确。

(2) 反码表示法

反码表示法可用原码来定义,设数X的原码表示法为[X],[X]表示X 的反码表示,则

[X]=[X]X>0

[X]=~[X]X<0   (1)

从公式(1)的定义可知反码表示法最高位同样是符号位,为0表示正数,为1表示负数,且有+0和-0之分。[+0]=00000000 00000000,[-0]=11111111 11111111。

例:+25和-25的补码表示

[+25]=[+25]=00000000 00011001

[-25]=11111111 11100110

即-25的反码表示为+25的原码表示按位取反所得的值。

(3) 补码表示法

设[X]表示数X的补码表示法,则定义如下:

[X]补=[X]X>0

[X]补=~[X]+1 X<0   (2)

按定义可以得到短整型数的表示如表2所示。

表2 带符号的短整型数补码表示

十进制数

补码表示

+0

00000000 00000000

+1

00000000 00000001

+2

00000000 00000010

+32767

01111111 11111111

-32768

10000000 00000000

-32767

10000000 00000001

-2

11111111 11111110

-1

11111111 11111111

负数的补码表示还可用下列公式表示:

[X]=2n+X X<0   (3)

由式(2)中的定义求补码的方法如下:

正数的补码即为其原码表示,负数的补码为先求出其反码,再在最低位加1。

例如[25]和[-25]的求解过程如下:

[25]=[25]=00000000 00011001

[-25]= [-25]+1+1=11111111 11100110+1=11111111 11100111=0xffe7

由公式(3)求补码的方法更为简单,但是必须知道数的表示位数n,过程如下:

[-25]=2n-25=0x10000-0x0019=0xffe7

补码运算有两个公式,如式(4)所示。

[X+Y]=[X]+[Y]

[X-Y]=[X]+[-Y](4)

从式(4)可以看出,两个数相加后的补码表示等于两个数补码之和,两个数相减的补码表示等于被减数的补码加上减数的相反数的补码数之和,即减法变成加法运算(在计算机硬件实现中这一点非常容易)。例如:

[25-26]=[25]+[-26]

=0x0019+0xffe6

=0xffff

=[-1]

(4) 短整型无符号数

无符号数最高位为有效的数据信息,表达的数据范围如表3所示

表3 带符号的短整型数补码表示

十进制数

补码表示

0

00000000 00000000

1

00000000 00000001

2

00000000 00000010

32767

01111111 11111111

32768

10000000 00000000

32769

10000000 00000001

65534

11111111 11111110

65535

11111111 11111111

3. 位运算举例

(1) 按位求反运算符 ~

它的优先级高于四则运算,它是按位对操作数求反,单目运算,从左至右结合。

设有如下程序段:

short int a=-25,b;

unsigned short c;

b=~a;

对于无符号数来说,按位取反仍然是无符号数;对于带符号数来说,负数按位取反就变成了正数。程序段运行结果如下:

~(-25)=24

分析结果:

由前面得知[-25]=0xffe7=11111111 11100111

按位取反等于 00000000 00011000=0x0018=24

(2) 移位运算符 << (左移) 和 >> (右移)

它的优先级高于关系运算,也是单目运算,从左至右结合。

格式:

<操作数> >>N 操作数右移N位

<操作数> <<N 操作数左移N位

左移1位操作数扩大一倍,即相对与乘2,右移1位操作数缩小1倍,相当于除2。

对于无符号数来说,左移时各位依次左移1位,最低位补0;右移时各位依次右移1位,最高位补0。

对于带符号数来说,左移时各位依次左移1位,最低位补0;右移时各位依次右移1位,最高位不变(补符号位)。

#include <stdio.h>

void main()

{

short int a=-32767,b;

unsigned short c=60000;

b=a<<1;

printf("%d<<%d=%d\n",a,1,b);

printf("%d>>%d=%d\n",c,1,c>>1);

}

运行结果

-32767<<1=2

60000>>1=30000

分析结果:

a为带符号的补码表示的数,[-32767]=10000000 00000001=0x8001,左移一位后

[-32767]<<1= 0000000 000000010=0x0002

最高位为符号位,本来是1表示负数,左移一位后,1移入到标志位CF(标志寄存器中的1位)中,结果变成了正数。

无符号数c各位依次右移一位后,最高位补0,结果等于除2。

带符号数右移时,除了各位要依次右移一位外,最高位补符号位,以保证正确性。

实际上左移一位各位权值加倍,右移一位各位权值减少一半。

(3) 优先级高于逻辑运算

按位逻辑运算符有按位或运算符| 按位异或运算符 ^ 和按位与运算符&,都是双目运算符,自左至右结合。

设有如下程序段:

short int x=-1124,y=2094,z1,z2,z3;

z1=x|y;

printf("%d|%d=%d\n",x,y,z1);

z2=x&y;

printf("%d&%d=%d\n",x,y,z2);

z3=x^y;

printf("%d^%d=%d\n",x,y,z3);

运算结果为:

-1124|2094=-1090

-1124&2094=2060

-1124^2094=-3150

分析结果如表4所示。

表4 或与异或位运算结果分析表

变量

十进制数

短整型补码表示(二进制)

短整型补码表示(十六进制)

X

-1124

11111011 10011100

0xfb9c

Y

2094

00001000 00101110

0x082e

z1=x|y

-1090

11111011 10111110

0xfbbe

z2=x&y

2060

00001000 00001100

0x080c

z3=x^y

-3150

11110011 10110010

0xf3b2

(4) 位运算符与赋值运算符结合

位运算符与赋值运算符可以组成复合赋值运算符。

例如: &=, |=, >>=, <<=, ^=等

例: a & = b相当于 a = a & b

  a << =2相当于a = a << 2

本文以短整型数为例,介绍了在微机中数的表示特别是带符号数的表示,介绍了位运算符的运算,并对实例结果进行了分析。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值