C语言第17章 位运算操作符
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
个字节表示。
17.1.2 补码
2.负数的补码 负数的补码的符号位为1,其余位为将该数绝对值的原码按位取反
后再加1的结果。例如,-15的补码:因为是负数,则符号位为 “1”,整个为10001111;其余7位为-15的绝对值的原码按位取 反,即0001111取反后为1110000,再加1为1110001;加上符 号位,最后-15的补码是11110001。已知一个数的补码,其求 原码的过程与已知原码求补码的过程完全一样: 如果补码的符号位为“0”,表示是一个正数,所以补码就是该数 的原码。 如果补码的符号位为“1”,表示是一个负数,求原码的操作可以 是:符号位为1,其余各位取反加1。 使用补码计算时,可以将符号位和其他位统一处理。
17.2.7 位运算赋值操作符
其功能与+=和-=等复合赋值操作符类似。例如: a >>= 3; 等效于 a = a >> 3; 又例如: a &= b; 等效于 a=a&b 范例17-7第20行也可以改写为: flag >>= 1;
17.3 位运算操作符使用举例
位运算操作符在C语言中的使用十分灵活,通过一些巧妙地 算法可以高效地实现一些实用的功能。为了更熟悉位运算 操作符的使用,本节将继续讨论两个例子。
17.2.3 位与操作符(&)
例如,计算39 & 15,即00100111和00001111逐位进行位与 运算,得到结果00000111,即7,其过程如下图所示。
17.2.3 位与操作符(&)
位与操作符有很多特殊的用途: 1.将数值清零 如果用0与任何数进行位与运算,可以将该数清零。例如: int a = 39; a = a & 0; 运算过程如右图所示。
3.一个数与本身异或得到0 一个数与其本身肯定是相同的,所以不为“异”,则异或结
果为0。例如: int a = 39; a = a ^ a; 执行该段代码后a为0,运算过程如右图所示。
17.2.5 右移操作符(>>)
右移操作符的作用是将一个数的各二进制值全部右移若干位 。其使用形式如下:
数值1 >> 数值2; 该表达式将数值1的二进制值右移(数值2)位,移到右端的
17.1.2 补码
例如,7 + 15,其补码相加如左图所示。 (-7) + (-15),其补码相加如右图所示。
17.1.2 补码
注意:两个用补码表示的数相加时,如果最高位(符号位) 有进位,则进位被舍弃。
减法也可按加法来处理,7 – 15的计算过程如下图所示。
17.2 位运算操作符
C语言共提供了6个位运算操作符,包括取反操作符(~)、 位或操作符(|)、位与操作符(&)、异或操作符(^) 和位移操作符(>>和<<)。每一个操作符都有各自独特 的作用。本节将依次介绍各个操作符的语法,并通过范例 来讲解各个操作符的使用,在最后还简要地介绍各个操作 符与赋值操作符组合而成的位运算赋值操作符的使用。
17.3.2 使用子网掩码
子网掩码是一个32位地址,用于屏蔽IP地址的一部分以区别 网络标识和主机标识,并说明该IP地址是在局域网上,还 是在外部网上。如果一个IP地址与子网掩码相与的结果与 一个子网地址相同,那么这个IP地址便属于该子网。有两 个子网地址59.64.200.0和59.64.201.0,两个IP地址 59.64.200.18和59.64.201.20,子网掩码位 255.255.255.0。要求判断它们的归属。
17.2.2 位或操作符(|)
位或操作符有以下两种用途: 1.对指定二进制位的赋值 位或操作符可以实现对数值指定二进制位赋值为1。例如,
要将a的第4位置为1,可以使用以下代码: int a = 39; a = a | 0x0f; 由于0x0f低4位全为1,因此与该值位或操作后,a的低4位被
置为1,其余位值不变。
17.2.2 位或操作符(|)
~0是取得二进制位全为1的数值,如果该代码运行的系统中 int占用空间为4字节,则该值为0xffffffff;如果,int占用 空间为2字节,则该值为0xffff。这种获取二进制值为全1 的方式移植性好。
~(0x04)的二进制值除了第3位为0外,其余位值都为1。将该 值与a值进行位或运算,可以将a除第3位外的所有位置为 1。此时,如果a的第3位为1,则(a | ~(0x04))的二进制 值为全1,即~0;否则,与~0不等。
17.2.6 左移操作符(<<)
提示:这里讨论的整数都只占用1个字节。 从上图中可以看出,将a左移1位,即等于将a乘以2;将a左
移n位,即等于将a乘以2的n次方。左移的效率比乘法效 率高。
17.2.7 位运算赋值操作符
位运算赋值操作符就是将各个位操作符与赋值操作符组合在 一起,共有以下5种:
|= &= ^= >>= <<=
17.2.1 取反操作符(~)
取反操作符是一个一元操作符,其使用形式为: ~操作数; 该操作数可以为常量,也可以为变量。取反操作符可以将数
按位取反,即将0变为1,将1变为0。例如,~15是对15的 在内存中的存储形式按位取反,如下图所示。
17.2.2 位或操作符(|)
位或操作符是一个二元操作符,形式如下: 数1 | 数2 同样,这两个数可以是常量,也可以是变量。位或操作符将
17.2.3 位与操作符(&)
要取得第1个字节的高4位,可以如下:
b = a & 0xf0; 其中,0xf0的二进制形式的第5位到第8位位1,与其位与后
,只保留该4位的信息,其余各位都置为0,运算过程如 下图所示。
17.2.3 位与操作符(&)
若要只获取一个位数的二进制值,例如获取第3位,可以使 用以下代码:
17.2.3 位与操作符(&)
2.取指定位数的二进制数 使用位与操作可以获得数值上指定位的信息。例如,要获得
一个整数的第低4位,可以如下: int a = 39; int b; … b = a & 0x0f;
17.2.3 位与操作符(&)
其中,0x0f的二进制形式只有后4位为1,与其位与运算后, 只保留最低4位的信息,其余位都被置为0,运算过程如 下图所示。
如果是有符号数,则不同的系统有不同的处理方式。有的系 统补入0,有的补入符号位。补入0的为“逻辑右移”, 补入原符号位的为“算术右移”。例如,将-15右移两位 ,算术右移后结果为11111100,逻辑右移结果为 00111100,如下图所示。
17.2.6 左移操作符(<<)
左移操作符将一个数的各个二进制位值全部左移若干位。使 用形式如下:
17.1.2 补码
在操作系统中,数值一律用补码来存储。一个数值的二进制 值可以称其为原码,存储时会将原码表示为补码。补码的 最高位为符号位,数值的补码表示可以分为以下两种情况 。
17.1.2 补码
1.非负数的补码 非负数的补码与原码相同。 例如,11的原码为00001011,其补码也为00001011。 注意:为了简化书写,讨论位运算时,大部分的数值只用一
两个操作数逐位进行位或运算。两个位值进行位或运算的 规则是:只要有一个数值为1,位或的结果便为1;如果 都为0,位或的结果为0。位或运算结果如下: 1|1=1 1|0=1 0|1=1 0|0=0
17.2.2 位或操作符(|)
例如,运算39 | 15,即00100111和00001111逐位进行位或 运算,得到结果00101111,即47。过程如下图所示。
字节在操作系统中可以分为更小的单元“位”。大部分系统 中,1个字节由8个位组成,每个位的值为0或1。一个字 节的数值由其8个位的值决定,如果一个字节的内容为 “01101010”。其对应的值为:
26 25 23 21 64 32 8 2 106
这个字节的值为8个位组成的二进制数。但是在操作系统中 ,数值的存储并不是直接以其二进制值存储的。
低位被舍弃,而高位补入的数值由符号位决定。如果是无 符号数,则高位补入的数为0。例如: int a = 39;
a = a >> 2;
17.2.5 右移操作符(>>)
则变量a将右移两位,即00100111右移两位,得到 00001001,右端的两个1被舍弃。如下图所示。
17.2.5 右移操作符(>>)
17.2.4 异或操作符(^)
例如,39与15异或,即00100111与00001111异或,结果如 下图所示。
17.2.4 异或操作符(^)
可以将异或运行理解为,判断两个数相应位值是否为“异” :若为“异”,则结果为1;否则,结果为0。异或操作 符有以下特性。
1.与0异或,不改变数值 例如,将39与0异或,不会改变39的值,如下图所示。
数值1 << 数值2; 该表达式将数值1的二进制值左移(数值2)位,移到左端的
高位被舍弃,而低位补入的数值为0。例如: int a = 39; int b = a << 1; int c = a << 2; int d = a << 3;
17.Fra Baidu bibliotek.6 左移操作符(<<)
a的值为39,即00100111;b的值为39左移1位的结果,左 端舍去1个0,右端补入1个0,得到01001110;c值为39 左移2位的结果,左端舍去2个0,右端补入2个0,得到 10011100;d值为39左移3位的结果,左端舍去2个0和1 个1,右端补入3个0,得到00111000,如下图所示。
17.2.3 位与操作符(&)
位与操作符也是一个二元操作符,形式如下: 数1 & 数2 这两个数可以是常量,也可以是变量。位与操作符将两个操
作数逐位进行位与运算。位与运算的规则是:只要有一个 数值为0,位与的结果便为0;如果都为1,位与的结果为 1。位与运算结果如下: 1&1=1 1&0=0 0&1=0 0&0=0
17.3.1 循环移位
例如要对a(无符号数)循环右移3位,可以采用以下方案: (1)首先把将要移出右端的3个位左移8-3位(假定a占8个位)后
保存在b中,此时b形式如下图第2行所示,其中yyy是a的后3 位。 (2)将a右移3位,右端3位被舍弃,又由于a为无符号数,左端添 加3个0,如下图第3行所示。 (3)最后将a与b位或,由于0和任意值位或补改变该值,因此得 到yyyxxxxx,如下图第4行所示。
b = a & 0x04; 由于0x04第3位为1,则与a相与后,结果只保留a的第3位数
,其余都为0,如下图所示。
17.2.4 异或操作符(^)
异或操作符是多元操作符,使用形式如下: 数1 ^ 数2 表达式将数1和数2进行异或运算,如果两个数一样,则值为
0;如果不一样,值为1。运算如下: 1^1=0 1^0=1 0^1=1 0^0=0
17.1.1 字节与位
在前面的介绍中已多次涉及到字节的概念。操作系统中的各 种类型的数值都由若干个字节组成,字节也是数据存取和 数值计算的基本单元。从内存中读取数据是以字节为最小 单位,向内存中写入数据也是以字节为最小单位。将数值 输出到文件和从文件获取数值时,最小单位也是字节。
17.1.1 字节与位
17.2.4 异或操作符(^)
2.由于与1异或,会取其相反值 使用异或的这个特性,可以实现对部分位值取反。例如若要
对a的后7位取反,可以如下: int a = 39; a = a ^ 0x7f; 由于0x7f的后7位为1,那么与其异或可以将其后7位取反,
运算结果如下图所示。
17.2.4 异或操作符(^)
第17章 位运算操作符
在本章的学习中,需要掌握以下内容: 字节和位的概念; 原码与补码的转换; 6种位操作符及其组成的位运算赋值操作符的使用; 各个位操作符的特殊用途; 位域的概念和位域的使用。
17.1 位运算
位运算是指对数据的二进制位进行处理的运算。位运算与数 据在内存中的存储方式息息相关,因此在介绍位运算操作 符之前,有必要先介绍一些操作系统中数据存储和处理的 相关知识。本节首先介绍字节与位的概念以及两者的关系 ,再介绍数据在内存中的储存方式。
17.2.2 位或操作符(|)
2.判定指定二进制位的值 当位或操作符与取反操作符结合在一起可以判断指定数位的
值,例如要判断一个整数a其二进制第3位的值,可以如 下表示: if ( ~0 == a | ~(0x04)) printf(“a 的第3位为1”); else printf(“a 的第3位为0”);
17.3.1 循环移位
循环移位是一种重要的移位方式。循环移位区别于一般移位 的是移位时没有数位的丢失。循环左移时,用从左边移出 的位填充字的右端;而循环右移时,用从右边移出的位填 充字的左侧。本题暂只讨论循环右移。
例如,要将39循环右移2位,即00100111右移两位,移到右 端的两个1不会丢失,将补到左端的2个空缺位置上,如 下图所示。
17.1.2 补码
2.负数的补码 负数的补码的符号位为1,其余位为将该数绝对值的原码按位取反
后再加1的结果。例如,-15的补码:因为是负数,则符号位为 “1”,整个为10001111;其余7位为-15的绝对值的原码按位取 反,即0001111取反后为1110000,再加1为1110001;加上符 号位,最后-15的补码是11110001。已知一个数的补码,其求 原码的过程与已知原码求补码的过程完全一样: 如果补码的符号位为“0”,表示是一个正数,所以补码就是该数 的原码。 如果补码的符号位为“1”,表示是一个负数,求原码的操作可以 是:符号位为1,其余各位取反加1。 使用补码计算时,可以将符号位和其他位统一处理。
17.2.7 位运算赋值操作符
其功能与+=和-=等复合赋值操作符类似。例如: a >>= 3; 等效于 a = a >> 3; 又例如: a &= b; 等效于 a=a&b 范例17-7第20行也可以改写为: flag >>= 1;
17.3 位运算操作符使用举例
位运算操作符在C语言中的使用十分灵活,通过一些巧妙地 算法可以高效地实现一些实用的功能。为了更熟悉位运算 操作符的使用,本节将继续讨论两个例子。
17.2.3 位与操作符(&)
例如,计算39 & 15,即00100111和00001111逐位进行位与 运算,得到结果00000111,即7,其过程如下图所示。
17.2.3 位与操作符(&)
位与操作符有很多特殊的用途: 1.将数值清零 如果用0与任何数进行位与运算,可以将该数清零。例如: int a = 39; a = a & 0; 运算过程如右图所示。
3.一个数与本身异或得到0 一个数与其本身肯定是相同的,所以不为“异”,则异或结
果为0。例如: int a = 39; a = a ^ a; 执行该段代码后a为0,运算过程如右图所示。
17.2.5 右移操作符(>>)
右移操作符的作用是将一个数的各二进制值全部右移若干位 。其使用形式如下:
数值1 >> 数值2; 该表达式将数值1的二进制值右移(数值2)位,移到右端的
17.1.2 补码
例如,7 + 15,其补码相加如左图所示。 (-7) + (-15),其补码相加如右图所示。
17.1.2 补码
注意:两个用补码表示的数相加时,如果最高位(符号位) 有进位,则进位被舍弃。
减法也可按加法来处理,7 – 15的计算过程如下图所示。
17.2 位运算操作符
C语言共提供了6个位运算操作符,包括取反操作符(~)、 位或操作符(|)、位与操作符(&)、异或操作符(^) 和位移操作符(>>和<<)。每一个操作符都有各自独特 的作用。本节将依次介绍各个操作符的语法,并通过范例 来讲解各个操作符的使用,在最后还简要地介绍各个操作 符与赋值操作符组合而成的位运算赋值操作符的使用。
17.3.2 使用子网掩码
子网掩码是一个32位地址,用于屏蔽IP地址的一部分以区别 网络标识和主机标识,并说明该IP地址是在局域网上,还 是在外部网上。如果一个IP地址与子网掩码相与的结果与 一个子网地址相同,那么这个IP地址便属于该子网。有两 个子网地址59.64.200.0和59.64.201.0,两个IP地址 59.64.200.18和59.64.201.20,子网掩码位 255.255.255.0。要求判断它们的归属。
17.2.2 位或操作符(|)
位或操作符有以下两种用途: 1.对指定二进制位的赋值 位或操作符可以实现对数值指定二进制位赋值为1。例如,
要将a的第4位置为1,可以使用以下代码: int a = 39; a = a | 0x0f; 由于0x0f低4位全为1,因此与该值位或操作后,a的低4位被
置为1,其余位值不变。
17.2.2 位或操作符(|)
~0是取得二进制位全为1的数值,如果该代码运行的系统中 int占用空间为4字节,则该值为0xffffffff;如果,int占用 空间为2字节,则该值为0xffff。这种获取二进制值为全1 的方式移植性好。
~(0x04)的二进制值除了第3位为0外,其余位值都为1。将该 值与a值进行位或运算,可以将a除第3位外的所有位置为 1。此时,如果a的第3位为1,则(a | ~(0x04))的二进制 值为全1,即~0;否则,与~0不等。
17.2.6 左移操作符(<<)
提示:这里讨论的整数都只占用1个字节。 从上图中可以看出,将a左移1位,即等于将a乘以2;将a左
移n位,即等于将a乘以2的n次方。左移的效率比乘法效 率高。
17.2.7 位运算赋值操作符
位运算赋值操作符就是将各个位操作符与赋值操作符组合在 一起,共有以下5种:
|= &= ^= >>= <<=
17.2.1 取反操作符(~)
取反操作符是一个一元操作符,其使用形式为: ~操作数; 该操作数可以为常量,也可以为变量。取反操作符可以将数
按位取反,即将0变为1,将1变为0。例如,~15是对15的 在内存中的存储形式按位取反,如下图所示。
17.2.2 位或操作符(|)
位或操作符是一个二元操作符,形式如下: 数1 | 数2 同样,这两个数可以是常量,也可以是变量。位或操作符将
17.2.3 位与操作符(&)
要取得第1个字节的高4位,可以如下:
b = a & 0xf0; 其中,0xf0的二进制形式的第5位到第8位位1,与其位与后
,只保留该4位的信息,其余各位都置为0,运算过程如 下图所示。
17.2.3 位与操作符(&)
若要只获取一个位数的二进制值,例如获取第3位,可以使 用以下代码:
17.2.3 位与操作符(&)
2.取指定位数的二进制数 使用位与操作可以获得数值上指定位的信息。例如,要获得
一个整数的第低4位,可以如下: int a = 39; int b; … b = a & 0x0f;
17.2.3 位与操作符(&)
其中,0x0f的二进制形式只有后4位为1,与其位与运算后, 只保留最低4位的信息,其余位都被置为0,运算过程如 下图所示。
如果是有符号数,则不同的系统有不同的处理方式。有的系 统补入0,有的补入符号位。补入0的为“逻辑右移”, 补入原符号位的为“算术右移”。例如,将-15右移两位 ,算术右移后结果为11111100,逻辑右移结果为 00111100,如下图所示。
17.2.6 左移操作符(<<)
左移操作符将一个数的各个二进制位值全部左移若干位。使 用形式如下:
17.1.2 补码
在操作系统中,数值一律用补码来存储。一个数值的二进制 值可以称其为原码,存储时会将原码表示为补码。补码的 最高位为符号位,数值的补码表示可以分为以下两种情况 。
17.1.2 补码
1.非负数的补码 非负数的补码与原码相同。 例如,11的原码为00001011,其补码也为00001011。 注意:为了简化书写,讨论位运算时,大部分的数值只用一
两个操作数逐位进行位或运算。两个位值进行位或运算的 规则是:只要有一个数值为1,位或的结果便为1;如果 都为0,位或的结果为0。位或运算结果如下: 1|1=1 1|0=1 0|1=1 0|0=0
17.2.2 位或操作符(|)
例如,运算39 | 15,即00100111和00001111逐位进行位或 运算,得到结果00101111,即47。过程如下图所示。
字节在操作系统中可以分为更小的单元“位”。大部分系统 中,1个字节由8个位组成,每个位的值为0或1。一个字 节的数值由其8个位的值决定,如果一个字节的内容为 “01101010”。其对应的值为:
26 25 23 21 64 32 8 2 106
这个字节的值为8个位组成的二进制数。但是在操作系统中 ,数值的存储并不是直接以其二进制值存储的。
低位被舍弃,而高位补入的数值由符号位决定。如果是无 符号数,则高位补入的数为0。例如: int a = 39;
a = a >> 2;
17.2.5 右移操作符(>>)
则变量a将右移两位,即00100111右移两位,得到 00001001,右端的两个1被舍弃。如下图所示。
17.2.5 右移操作符(>>)
17.2.4 异或操作符(^)
例如,39与15异或,即00100111与00001111异或,结果如 下图所示。
17.2.4 异或操作符(^)
可以将异或运行理解为,判断两个数相应位值是否为“异” :若为“异”,则结果为1;否则,结果为0。异或操作 符有以下特性。
1.与0异或,不改变数值 例如,将39与0异或,不会改变39的值,如下图所示。
数值1 << 数值2; 该表达式将数值1的二进制值左移(数值2)位,移到左端的
高位被舍弃,而低位补入的数值为0。例如: int a = 39; int b = a << 1; int c = a << 2; int d = a << 3;
17.Fra Baidu bibliotek.6 左移操作符(<<)
a的值为39,即00100111;b的值为39左移1位的结果,左 端舍去1个0,右端补入1个0,得到01001110;c值为39 左移2位的结果,左端舍去2个0,右端补入2个0,得到 10011100;d值为39左移3位的结果,左端舍去2个0和1 个1,右端补入3个0,得到00111000,如下图所示。
17.2.3 位与操作符(&)
位与操作符也是一个二元操作符,形式如下: 数1 & 数2 这两个数可以是常量,也可以是变量。位与操作符将两个操
作数逐位进行位与运算。位与运算的规则是:只要有一个 数值为0,位与的结果便为0;如果都为1,位与的结果为 1。位与运算结果如下: 1&1=1 1&0=0 0&1=0 0&0=0
17.3.1 循环移位
例如要对a(无符号数)循环右移3位,可以采用以下方案: (1)首先把将要移出右端的3个位左移8-3位(假定a占8个位)后
保存在b中,此时b形式如下图第2行所示,其中yyy是a的后3 位。 (2)将a右移3位,右端3位被舍弃,又由于a为无符号数,左端添 加3个0,如下图第3行所示。 (3)最后将a与b位或,由于0和任意值位或补改变该值,因此得 到yyyxxxxx,如下图第4行所示。
b = a & 0x04; 由于0x04第3位为1,则与a相与后,结果只保留a的第3位数
,其余都为0,如下图所示。
17.2.4 异或操作符(^)
异或操作符是多元操作符,使用形式如下: 数1 ^ 数2 表达式将数1和数2进行异或运算,如果两个数一样,则值为
0;如果不一样,值为1。运算如下: 1^1=0 1^0=1 0^1=1 0^0=0
17.1.1 字节与位
在前面的介绍中已多次涉及到字节的概念。操作系统中的各 种类型的数值都由若干个字节组成,字节也是数据存取和 数值计算的基本单元。从内存中读取数据是以字节为最小 单位,向内存中写入数据也是以字节为最小单位。将数值 输出到文件和从文件获取数值时,最小单位也是字节。
17.1.1 字节与位
17.2.4 异或操作符(^)
2.由于与1异或,会取其相反值 使用异或的这个特性,可以实现对部分位值取反。例如若要
对a的后7位取反,可以如下: int a = 39; a = a ^ 0x7f; 由于0x7f的后7位为1,那么与其异或可以将其后7位取反,
运算结果如下图所示。
17.2.4 异或操作符(^)
第17章 位运算操作符
在本章的学习中,需要掌握以下内容: 字节和位的概念; 原码与补码的转换; 6种位操作符及其组成的位运算赋值操作符的使用; 各个位操作符的特殊用途; 位域的概念和位域的使用。
17.1 位运算
位运算是指对数据的二进制位进行处理的运算。位运算与数 据在内存中的存储方式息息相关,因此在介绍位运算操作 符之前,有必要先介绍一些操作系统中数据存储和处理的 相关知识。本节首先介绍字节与位的概念以及两者的关系 ,再介绍数据在内存中的储存方式。
17.2.2 位或操作符(|)
2.判定指定二进制位的值 当位或操作符与取反操作符结合在一起可以判断指定数位的
值,例如要判断一个整数a其二进制第3位的值,可以如 下表示: if ( ~0 == a | ~(0x04)) printf(“a 的第3位为1”); else printf(“a 的第3位为0”);
17.3.1 循环移位
循环移位是一种重要的移位方式。循环移位区别于一般移位 的是移位时没有数位的丢失。循环左移时,用从左边移出 的位填充字的右端;而循环右移时,用从右边移出的位填 充字的左侧。本题暂只讨论循环右移。
例如,要将39循环右移2位,即00100111右移两位,移到右 端的两个1不会丢失,将补到左端的2个空缺位置上,如 下图所示。