【C语言】按位逻辑运算符笔记

4个按位逻辑运算符都用于整型数据,包括char。之所以叫作按位(bitwise)运算,是因为这些操作都是针对每一个位进行,不影响它左右两边的位。不要把这些运算符与常规的逻辑运算符(&&、|| 和 !)混淆,常规的逻辑运算符操作的是整个值。

1.二进制反码或按位取反:~

一元运算符~把1变为0,把0变为1。如下例子所示:

~(10011010) // 表达式
(01100101)  // 结果值

假设val的类型是unsigned char,已被赋值为2。在二进制中,00000010 表示2。那么,~val的值是11111101,即253。注意,该运算符不会改变val 的值,就像3 * val不会改变val的值一样, val仍然是2。但是,该运算符确实 创建了一个可以使用或赋值的新值:

newval = ~val;
printf("%d", ~val);

如果要把val的值改为~val,使用下面这条语句:

val=~fsval

2.按位与:&

二元运算符&通过逐位比较两个运算对象,生成一个新值。对于每个 位,只有两个运算对象中相应的位都为1时,结果才为1(从真/假方面看, 只有当两个位都为真时,结果才为真)。因此,对下面的表达式求值:

(10010011) & (00111101)  // 表达式

由于两个运算对象中编号为4和0的位都为1,得:

(00010001)  // 结果值

C有一个按位与和赋值结合的运算符:&=。下面两条语句产生的最终结 果相同:

val &= 0377;
val = val & 0377;

3.按位或:|

二元运算符|,通过逐位比较两个运算对象,生成一个新值。对于每个 位,如果两个运算对象中相应的位为1,结果就为1(从真/假方面看,如果 两个运算对象中相应的一个位为真或两个位都为真,那么结果为真)。因 此,对下面的表达式求值:

(10010011) | (00111101) // 表达式

除了编号为6的位,这两个运算对象的其他位至少有一个位为1,得:

(10111111) // 结果值

C有一个按位或和赋值结合的运算符:|=。下面两条语句产生的最终作 用相同:

val |= 0377;
val = val | 0377;

4.按位异或:^

二元运算符^逐位比较两个运算对象。对于每个位,如果两个运算对象中相应的位一个为1(但不是两个为1),结果为1(从真/假方面看,如果两个运算对象中相应的一个位为真且不是两个为同为1,那么结果为真)。简答的一句话,值不一样时异或的结果为1,值一样时异或的结果为0。
因此,对下面表达式求值:

(10010011) ^ (00111101) // 表达式

编号为0的位都是1,所以结果为0,得:

(10101110)  // 结果值

C有一个按位异或和赋值结合的运算符:^=。下面两条语句产生的最终 作用相同:

val ^= 0377;
val = val ^ 0377;

用法:掩码 (&=)

所谓掩码指的是一些设置为开 (1)或关(0)的位组合。要明白称其为掩码的原因,先来看通过&把一个量与掩码结合后发生什么情况。例如,假设定义符号常量MASK为2 (即, 二进制形式为000010),只有1号位是1,其他位都是0。下面的语句:

flags = flags & MASK;

把flags中除1号位以外的所有位都设置为0,因为使用按位与运算符 (&)任何位与0组合都得0。1号位的值不变(如果1号位是1,那么 1&1得1;如果 1号位是0,那么 0&1也得0)。这个过程叫作“使用掩码”,因为掩码中的0隐藏了flags中相应的位。

可以这样类比:把掩码中的0看作不透明,1看作透明。表达式flags & MASK相当于用掩码覆盖在flags的位组合上,只有MASK为1的位才可见。

下面这条语句是按位与的一种常见用法:

ch &= 0xff; /* 或者 ch &= 0377; */

前面介绍过oxff的二进制形式是11111111,八进制形式是0377。这个掩码保持ch中最后8位不变,其他位都设置为0。无论ch原来是8位、16位或是 其他更多位,最终的值都被修改为1个8位字节。在该例中,掩码的宽度为8位。

用法:打开位(设置位)(|=)

有时,需要打开一个值中的特定位,同时保持其他位不变。例如,一台 IBM PC 通过向端口发送值来控制硬件。例如,为了打开内置扬声器,必须 打开 1 号位,同时保持其他位不变。这种情况可以使用按位或运算符 (|)。

以上一节的flags和MASK(只有1号位为1)为例。下面的语句:

flags = flags | MASK;

把flags的1号位设置为1,且其他位不变。因为使用|运算符,任何位与0 组合,结果都为本身;任何位与1组合,结果都为1。

这种方法根据MASK中为1的位,把flags中对应的位设置为1,其他位不变。

用法:关闭位(清空位)( &= ~)

和打开特定的位类似,有时也需要在不影响其他位的情况下关闭指定的位。假设要关闭变量flags中的1号位。同样,MASK只有1号位为1(即,打开)。可以这样做:

flags = flags &~ MASK; /* tip:逻辑运算符遵循右结合

由于MASK除1号位为1以外,其他位全为0,所以~MASK除1号位为0 以外,其他位全为1。使用&,任何位与1组合都得本身,所以这条语句保持 1号位不变,改变其他各位。另外,使用&,任何位与0组合都的0。所以无 论1号位的初始值是什么,都将其设置为0。

***MASK中为1的位在结果中都被设置(清空)为0。flags中与MASK为0的位相应的位在结果中都未改变。***与掩码(&=)相反。

简化:

flags &= ~MASK;

用法:切换位(^=)

切换位指的是打开已关闭的位,或关闭已打开的位。可以使用按位异或运算符( ^ )切换位。也就是说,假设b是一个位(1或0),如果b为1,则1b为0;如果b为0,则1b为1。另外,无论b为1还是0,0^ b均为b。因此,如果使用^组合一个值和一个掩码,将切换该值与MASK为1的位相对应的 位,该值与MASK为0的位相对应的位不变。要切换flags中的1号位,可以使用 下面两种方法:

flags = flags ^ MASK;
flags ^= MASK;

flags中与MASK为1的位相对应的位都被切换了,MASK为0的位相对应 的位不变。

用法:检查位的值

有时,需要检查某位的值。

例如,flags中 1号位是否被设置为1?不能这样直接比较flags和MASK:

if (flags == MASK)
puts("Wow!"); /* 不能正常工作 */

这样做即使flags的1号位为1,其他位的值会导致比较结果为假。因此,必须覆盖flags中的其他位,只用1号位和MASK比较:

if ((flags & MASK) == MASK)
puts("Wow!");

由于按位运算符的优先级比==低,所以必须在flags & MASK周围加上 圆括号。

为了避免信息漏过边界,掩码至少要与其覆盖的值宽度相同。

移位运算符

移位运算符向左或向右移动位。

左移:<<
左移运算符(<<)将其左侧运算对象每一位的值向左移动其右侧运算对象指定的位数。左侧运算对象移出左末端位的值丢失,用0填充空出的位 置。下面的例子中,每一位都向左移动两个位置:

(10001010) << 2  // 表达式
(00101000)    // 结果值

该操作产生了一个新的位值,但是不改变其运算对象。例如,假设 stonk为1,那么 stonk<<2为4,但是stonk本身不变,仍为1。可以使用左移赋 值运算符(<<=)来更改变量的值。该运算符将变量中的位向左移动其右侧 运算对象给定值的位数。如下例:

int stonk = 1;
int onkoo;
onkoo = stonk << 2;  /* 把4赋给onkoo */
stonk <<= 2;     /* 把stonk的值改为4 */

右移:>>
右移运算符(>>)将其左侧运算对象每一位的值向右移动其右侧运算 对象指定的位数。左侧运算对象移出右末端位的值丢。对于无符号类型,用0填充空出的位置;对于有符号类型,其结果取决于机器。空出的位置可用 0填充,或者用符号位(即,最左端的位)的副本填充:

(10001010) >> 2    // 表达式,有符号值
(00100010)       // 在某些系统中的结果值
(10001010) >> 2    // 表达式,有符号值
(11100010)       // 在另一些系统上的结果值
//下面是无符号值的例子:
(10001010) >> 2    // 表达式,无符号值
(00100010)       // 所有系统都得到该结果值
//每个位向右移动两个位置,空出的位用0填充。

右移赋值运算符(>>=)将其左侧的变量向右移动指定数量的位数。如 下所示:

int sweet = 16;
int ooosw;
ooosw = sweet >> 3;  // ooosw = 2,sweet的值仍然为16
sweet >>=3;      // sweet的值为2

用法:移位运算符

移位运算符针对2的幂提供快速有效的乘法和除法:

number << n    number乘以2的n次幂
number >> n    如果number为非负,则用number除以2的n次幂

这些移位运算符类似于在十进制中移动小数点来乘以或除以10。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值