C语言中的位级运算、逻辑运算和移位运算

C语言中的位级运算、逻辑运算和移位运算

位级运算

​ C语言的一个很有用的特性就是它支持按位布尔运算。事实上,我们在布尔运算中使用的那些符号就是C语言所使用的:|就是OR (或), &就是AND (与), ~就是NOT (取反),而^就是EXCLUSIVE-OR (异或),这些运算能运用到任何“整型”的数据类型上,也就是那些声明为char或者int的数据类型,无论它们有没有像short. long、long long或者unsigned这样的限定词。以下是一些对char数据类型表达式求值的例子。

在这里插入图片描述
布尔运算

​ 位级运算的一个常见用法就是实现掩码运算,这里掩码是一个位模式,表示从一个字中选出的位的集合。让我们来看一个例子,掩码0xFF (最低的8位为1)表示一个字的低位字节。位级运算x&0xFF生成一个由x的最低有效字节组成的值,而其他的字节就被置为0。比如,对于x=0x89ABCDEF,其表达式将得到0x000000EF表达式~0将生成一个全1的掩码,不管机器的字大小是多少。尽管对于一个32位机器来说,同样的掩码可以写成0xFFFFFFFF,但是这样的代码不是可移植的。

逻辑运算

​ C语言还提供了一组逻辑运算符||、 &&和! ,分别对应于命题逻辑中的OR,AND和NOT运算。逻辑运算很容易和位级运算相混淆,但是它们的功能是完全不同的。逻辑运算认为所有非零的参数都表示TRUE,而参数0表示FALSE.它们返回1或者0,分别表示结果为TRUE或者为FALSE。以下是一些表达式求值的示例。

表达式结果
!0x410x00
!0x000x01
!!0x410x01
0x69&&0x550x01
0x69||0x550x01

可以观察到,按位运算只有在特殊情况下,也就是参数被限制为0或者1时,才和与其对应的逻辑运算有相同的行为。 逻辑运算符&&和||与它们对应的位级运算&和|之间第二个重要的区别是,如果对第一个参数求值就能确定表达式的结果,那么逻辑运算符就不会对第二个参数求值。 因此,例如,表达式a&&5/a将不会造成被零除,而表达式p&&*p++也不会导致间接引用空指针。

举个栗子:

假设x和y的字节值分别为0x66和0x39. 填下表,指明各个C表达式的字节值.

表达式
x & y
x | y
~x | ~y
x & !y
x && y
x || y
!x||!y
x && ~y
  • 注意:C语言中的逻辑运算非零参数都是为1,即TRUE;而参数零表示FALSE,他们只会返回1和0.

答案:

表达式
x & y0x20
x | y0x7F
~x | ~y0xDF
x & !y0x00
x && y0x01
x || y0x01
!x||!y0x00
x && ~y0x01

再来个小练习:

只使用位级和逻辑运算,编写一个C表达式, 它等价于 x==y. 换句话说, 当x和y相等时它将返回1,否则返回0.

答案:

表达式: !(x^y)

当且仅当x的每一位和y相应的每一位匹配时, x ^ y 等于0,然后利用!来判定一个字是否包含任何非零位.

移位运算

C语言中的移位运算

​ C语言还提供了一组移位运算,以便向左或者向右移动位模式。对于一个位表示为[xn-1 , xn-2 ,… ,x0 ]的操作数x,C表达式 x<<k 会生成一个值,其位表示为[xn-k-1 , xn-k-2 , … ,x0 , … , 0]也就是说, x向左移动k位,丢弃最高的k位,并在右端补k个0。移位量应该是一个0 ~ n-1之间的值。移位运算是从左至右可结合的,所以x<<j<<k等价于(x<<j) << k。有一个相应的右移运算x>>k,但是它的行为有点微妙。一般而言,机器支持两种形式的右移:逻辑右移算术右移逻辑右移在左端补k个0, 得到的结果是[0, … , 0, xn-1 , xn-2 , … ,xk ]。算术右移是在左端补k个最高有效位的值,得到的结果是[xn-1 , … , xn-1 , Xn-1 , xn-2, … ,xk ]。这种做法看上去可能有点奇特,但是我们会发现它对有符号整数数据的运算非常有用。

注意:逻辑左移和算术左移是一样的,逻辑右移和算术右移是不一样的


附加:

  • 涉及计算机中的移位操作指令的问题

在计算机中只能以二进制的形式进行移位,向左移动 |n| 位,就相当于乘于2n ,其中n也分正负值,向左为正值,向右为负值。

算术左移,操作数左移,最低位补0,逻辑左移,操作数左移,最低位补0。

算术右移,操作数右移,最高位不变,逻辑右移,操作数右移,最高位补0。

左移操作相当于乘法操作,它是会让操作数变大的,也就是说,它随时存在着溢出的风险若左移操作保留符号位的话,那么左移会出现越来越小的情况,但是你的本意是让这个数变大最后却发现左移后的数变小了,为了规避这种溢出带来的问题,统一了左移操作,也就是说,在左移操作中,寄存器只负责储存这个数(不管是否合乎人意,它只要是个数就行),若没有溢出的时候,则原样存储。所以说左移操作是会改变符号位的。

我们知道右移操作的算术操作是除法,那么在计算机中是肯定让这个数变小的,所以不存在溢出的风险,那么设计者就把是否保留符号位的权力交给了程序编写人员,所以 SARSHR 是两个不同的操作

sal,sar,shl,shr (其中sal和shl功能完全等价)

维基百科中有介绍:

Logical shifts are best used with unsigned numbers
逻辑移位最好用于无符号数

In an arithmetic shift, the spaces are filled in such a way to preserve the sign of the number being slid. For this reason, arithmetic shifts are better suited for signed numbers in two’s complement format
在算术移位中,移位空间被补0或者保留高位(即符号位)的方式处理,因此,算术移位操作更适用于两个有符号数的补码操作

在这里插入图片描述

​ C语言标准并没有明确定义应该使用哪种类型的右移。对于无符号数据(也就是以限定词unsigned声明的整型对象),右移必须是逻辑的。而对于有符号数据(默认的声明的整型对象),算术的或者逻辑的右移都可以。不幸的是,这就意味着任何假设一种或者另一种右移形式,的代码都潜在着可移植性问题。然而,实际上,几乎所有的编译器/机器组合都对有符号数据使用算术右移,且许多程序员也都假设机器会使用这种右移。另一方面, Java对于如何进行右移有明确的定义。表达式x>>k会将x算术右移k个位置,而x>>>k会对x做逻辑右移。

在这里插入图片描述



在这里插入图片描述

举个栗子:

填下表,说明不同移位运算对单字节数的影响.思考移位的最好方式是使用二进制表示. 将最初的值转换为二进制执行移位运算,然后再转换回十六进制.每个答案都应该是8个二进制数字或者2个十六进制数字.

注:单字节数(一个字节位8位,因此只有8位二进制数),有溢出风险,可能会使得原本移位后变大的数经过移位后变小.

在这里插入图片描述

答案:

此例子可以帮助理解不同的移位运算

在这里插入图片描述

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值