首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

C语言位运算(按位与运算、或运算、异或运算、左移运算、右移运算)

正文共:6580 字 预计阅读时间10 分钟

引言

在前面的C语言学习中,我们的各种运算都是以字节为基本单位进行的,但在编写很多系统程序中,如调制解调程序、磁盘文件管理程序和打印机驱动程序等,常要求在位(bit)一级进行运算或处理。C语言提供了位运算功能,使得C语言也能像火遍语言一样用来编写系统程序。位运算应用于整型数据,即把整型数据看做固定的二进制序列,然后对这些二进制序列进行按位运算。与其他高级语言相比,位运算是C语言的特点之一。

什么是位运算

所谓位运算,就是对一个比特(Bit)位进行操作。比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。

C语言提供了六种位运算符如下表:

一、按位与运算(&)

一个比特(Bit)位只有 0 和 1 两个取值,只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0。例如1&1为 1,0&0为 0,1&0也为 0,这和逻辑运算符&&非常类似。C语言中不能直接使用二进制,&两边的操作数可以是十进制、八进制、十六进制,它们在内存中最终都是以二进制形式存储,&就是对这些内存中的二进制位进行运算。其他的位运算符也是相同的道理。例如,9 & 5可以转换成如下的运算:

  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0001  (1 在内存中的存储)

也就是说,按位与运算会对参与运算的两个数的所有二进制位进行&运算,9 & 5的结果为 1。

又如,-9 & 5可以转换成如下的运算:

  1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

& 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-9 & 5的结果是 5。

再强调一遍,&是根据内存中的二进制位进行运算的,而不是数据的二进制形式;其他位运算符也一样。以-9&5为例,-9 的在内存中的存储和 -9 的二进制形式截然不同:

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (-9 的二进制形式,前面多余的 0 可以抹掉)

按位与运算通常用来对某些位清 0,或者保留某些位。例如要把 n 的高 16 位清 0 ,保留低 16 位,可以进行n & 0XFFFF运算(0XFFFF 在内存中的存储形式为 0000 0000 -- 0000 0000 -- 1111 1111 -- 1111 1111)。

【实例】对上面的分析进行检验。

#include

int main(){

int n = 0X8FA6002D;

printf("%d, %d, %X\n", 9 & 5, -9 & 5, n & 0XFFFF);

return 0;

}

运行结果:

1, 5, 2D

二、按位或运算(|)

参与|运算的两个二进制位有一个为 1 时,结果就为 1,两个都为 0 时结果才为 0。例如1|1为1,0|0为0,1|0为1,这和逻辑运算中的||非常类似。

例如,9 | 5可以转换成如下的运算:

  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

|   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1101  (13 在内存中的存储)

9 | 5的结果为 13。

又如,-9 | 5可以转换成如下的运算:

  1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

|   0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-9 | 5的结果是 -9。

按位或运算可以用来将某些位置 1,或者保留某些位。例如要把 n 的高 16 位置 1,保留低 16 位,可以进行n | 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。

【实例】对上面的分析进行校验。

#include

int main(){

int n = 0X2D;

printf("%d, %d, %X\n", 9 | 5, -9 | 5, n | 0XFFFF0000);

return 0;

}

运行结果:

13, -9, FFFF002D

三、按位异或运算(^)

参与^运算两个二进制位不同时,结果为 1,相同时结果为 0。例如0^1为1,0^0为0,1^1为0。

例如,9 ^ 5可以转换成如下的运算:

  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

^  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1100  (12 在内存中的存储)

9 ^ 5的结果为 12。

又如,-9 ^ 5可以转换成如下的运算:

  1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

^  0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 0101  (5 在内存中的存储)

-----------------------------------------------------------------------------------

1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0010  (-14 在内存中的存储)

-9 ^ 5的结果是 -14。

按位异或运算可以用来将某些二进制位反转。例如要把 n 的高 16 位反转,保留低 16 位,可以进行n ^ 0XFFFF0000运算(0XFFFF0000 在内存中的存储形式为 1111 1111 -- 1111 1111 -- 0000 0000 -- 0000 0000)。

【实例】对上面的分析进行校验。

#include

int main(){

unsigned n = 0X0A07002D;

printf("%d, %d, %X\n", 9 ^ 5, -9 ^ 5, n ^ 0XFFFF0000);

return 0;

}

运行结果:

12, -14, F5F8002D

四、取反运算(~)

取反运算符~为单目运算符,右结合性,作用是对参与运算的二进制位取反。例如~1为0,~0为1,这和逻辑运算中的!非常类似。。

例如,~9可以转换为如下的运算:

~ 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1001  (9 在内存中的存储)

-----------------------------------------------------------------------------------

 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0110  (-10 在内存中的存储)

所以~9的结果为 -10。

例如,~-9可以转换为如下的运算:

~ 1111 1111 -- 1111 1111 -- 1111 1111 -- 1111 0111  (-9 在内存中的存储)

-----------------------------------------------------------------------------------

 0000 0000 -- 0000 0000 -- 0000 0000 -- 0000 1000  (9 在内存中的存储)

所以~-9的结果为 8。

【实例】对上面的分析进行校验。

#include

int main(){

printf("%d, %d\n", ~9, ~-9 );

return 0;

}

运行结果:

-10, 8

五、左移运算(<<)

左移运算符

例如,9

-----------------------------------------------------------------------------------

   0000 0000 -- 0000 0000 -- 0000 0000 -- 0100 1000  (72 在内存中的存储)

所以9

又如,(-9)

-----------------------------------------------------------------------------------

    1111 1111 -- 1111 1111 -- 1111 1111 -- 1011 1000  (-72 在内存中的存储)

所以(-9)

如果数据较小,被丢弃的高位不包含 1,那么左移 n 位相当于乘以 2 的 n 次方。

【实例】对上面的结果进行校验。

#include

int main(){

printf("%d, %d\n", 9

return 0;

}

运行结果:

72, -72

六、右移运算(>>)

右移运算符>>用来把操作数的各个二进制位全部右移若干位,低位丢弃,高位补 0 或 1。如果数据的最高位是 0,那么就补 0;如果最高位是 1,那么就补 1。

例如,9>>3可以转换为如下的运算:

所以9>>3的结果为 1。

又如,(-9)>>3可以转换为如下的运算:

所以(-9)>>3的结果为 -2

如果被丢弃的低位不包含 1,那么右移 n 位相当于除以 2 的 n 次方(但被移除的位中经常会包含 1)。

【实例】对上面的结果进行校验。

#include

int main(){

printf("%d, %d\n", 9>>3, (-9)>>3 );

return 0;

}

运行结果:

1, -2

补充二进制思想以及数据的存储

我们平时使用的数字都是由 0~9 共十个数字组成的,例如 1、9、10、297、952 等,一个数字最多能表示九,如果要表示十、十一、二十九、一百等,就需要多个数字组合起来。

例如表示 5+8 的结果,一个数字不够,只能”进位“,用 13 来表示;这时”进一位“相当于十,”进两位“相当于二十。

因为逢十进一,也因为只有 0~9 共十个数字,所以叫做十进(Decimalism)。

进制也就是进位制。在进行加法(减法)运算时,逢X进(借)一就是X进制,这种进制也就包含X个数字,基数为X。十进制有0~9共10个数字,基数为10,在加减法运算中,逢十进一,借一当十。

我们不妨将思维拓展一下,既然可以用 0~9 共十个数字来表示数值,那么也可以用0、1两个数字来表示数值,这就是二进制(Binary)。

二进制思想

二进制只有0和1两个数字,基数为2,在加减法运算中,逢二进一,借一当二。

表示数值:0、1、10、111、100、1000001

加法:1+0=1、1+1=10、10+110=1000、111+111=1110

减法:1-0=1、10-1=1、100-11=1、1010-101=101

二进制和十进制的转换:

十进制 4321 = 4×103+ 3×102+ 2×101+ 1×100

二进制 1101 = 1×23+ 1×22+ 0×21+ 1×20= 8 + 4 + 0 + 1 = 13

二进制 110.11 = 1×22+ 1×21+ 0×20+ 1×2-1+ 1×2-2= 4 + 2 + 0 + 0.5 + 0.25 = 6.75

在内存中,数据就是以二进制的形式存储的。

内存中数据的存储

计算机要处理的信息是多种多样的,如十进制数、文字、符号、图形、音频、视频等,这些信息在人们的眼里是不同的。但对于计算机来说,它们在内存中都是一样的,都是以二进制的形式来表示。

要想学习编程,就必须了解二进制,它是计算机处理数据的基础。

内存条是一个非常精密的部件,包含了上亿个电子元器件,它们很小,达到了纳米级别。这些元器件,实际上就是电路;电路的电压会变化,要么是 0V,要么是 5V,只有这两种电压。5V 是通电,用1来表示,0V 是断电,用0来表示。所以,一个元器件有2种状态,0 或者 1。

我们通过电路来控制这些元器件的通断电,会得到很多0、1的组合。例如,8个元器件有 28=256 种不同的组合,16个元器件有 216=65536 种不同的组合。虽然一个元器件只能表示2个数值,但是多个结合起来就可以表示很多数值了。

一般情况下我们不一个一个的使用元器件,而是将8个元器件看做一个单位,即使表示很小的数,例如 1,也需要8个,也就是 00000001。

1个元器件称为1比特(Bit)或1位,8个元器件称为1字节(Byte),那么16个元器件就是2Byte,32个就是4Byte,以此类推:

8×1024个元器件就是1024Byte,简写为1KB;

8×1024×1024个元器件就是1024KB,简写为1MB;

8×1024×1024×1024个元器件就是1024MB,简写为1GB。

现在,你知道1GB的内存有多少个元器件了吧。我们通常所说的文件大小是多少KB、多少MB,就是这个意思。

单位换算:

8 Bit = 1Byte

1024Byte = 1KB

1024KB = 1MB

1024MB = 1GB

1024GB = 1TB

你看,在内存中没有abc这样的字符,也没有gif、jpg这样的图片,只有0和1两个数字,计算机也只认识0和1。所以,计算机使用二进制,而不是我们熟悉的十进制,写入内存中的数据,都会被转换成0和1的组合。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230205A002CO00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券