位运算讲义
C语言程序设计课件:位运算

❖
补码:1 1 1 0 1 1 0 0
❖
求反:1 0 0 1 0 0 1 1
❖ +1
❖
原码:1 0 0 1 0 1 0 0
❖ 求得:[X]原=1 0 0 1 0 1 0 0B。
❖ 例:求18-15的值。 ❖ 利用补码,减法运算就转化为加法实现,变成了求[18-15 ]补,
[18-15 ]补等价为[18]补+[-15]补,先求-15 的补码,-15的二 进制表示为10001111,则-15 的补码为
❖ 反码表示规则:正数的反码与原码相同;负数的反码,符号位为 “1”不变,数值部分按位取反,即0变为1,1变为0。反码很少 直接用于计算机中,它是用于求补码的过程产物。
❖ 例如:00111000的反码为00111000,10111000的反码为11000111。 ❖ 补码的表示规则:正数的补码与原码相同;负数的补码是在反码
11.2.2位复合赋值运算符
C 语言提供了如表 11.2 所示的 5 种位复合赋值运算符。
表 11.2 位复合赋值运算符
运算符 含义
结合性
优先级(附录中等级)
&=
先对右值按位与,再赋值 自右向左
15
|=
先对右值按位或,再赋值 自右向左
15
^=
先对右值按位异或,再赋值 自右向左
15
<<=
先对右值左移,再赋值
00001101赋值给n,p的值二进制数为00000000 00000111,n&p的值对应二进制数为00000000 00000101赋值给变量t。
11.4错误解析
❖ 1.位运算要求操作数的数据类型为整型。 ❖ 2.左移运算将一个位串信息向左移指定的位,左端移出的位的
位运算

2.按位或──|
(1)格式:x | y (2)规则:对应位均为0时才为0,否则为1。 例如,15|9=15: 0000 0000 0000 1111 | 0000 0000 0000 1001 0000 0000 0000 1111=15 (3)主要用途: 将1个数的某(些)位置1,其余各位不变。
12.3 应用举例
[例12.1] 从键盘上输入1个正整数给int变量num,输出由8~ 11位构成的数(从低位、0号开始编号)。 基本思路: (1)使变量num右移8位,将8~11位移到低4位上。 (2)构造1个低4位为1、其余各位为0的整数。 (3)与num进行按位与运算。
main() { int num, mask; printf("Input a integer number: "); scanf("%d",&num); num >>= 8; /*右移8位,将8~11位移到低4位上*/ mask = ~ ( ~0 << 4); /*间接构造1个低4位为1、其余各位为0的整数*/ printf("result=0x%x\n", num & mask); }
3.按位异或──^
(1)格式:x ^ y (2)规则:对应位相同时为0,不同时为1:15^9=6。 0000 0000 0000 1111 ^ 0000 0000 0000 1001 0000 0000 0000 0110=6 (3)主要用途: 使一个数的某(些)位翻转(即原来为1的位变为0,为0的 变为1),其余各位不变。
种情况: (1)正数的反码:与原码相同。 例如,+9的反码是00000000 00001001。 (2)负数的反码:符号位为1,其余各位为该数绝对 值的原码按位取反(1变0、0变1)。 例如:-9的原码 10000000 00001001 按位取反为 11111111 11110110 -9的反码是 11111111 11110110 16位二进制的整数,其反码的数值范围为: 10000000 00000000 ~ 11111111 11111111(-32767~ -0) 00000000 00000000 ~ 01111111 11111111(+0~ +32767)
第11讲位运算

2009年3月
1、有以下程序 、 #include <stdio.h> main() { int } 程序运行后的输出结果是 A)21 B)11 C)6 D)1 a=5, b=1, t; t=(a<<2)|b; printf("%d\n", t);
3.按位异或──^ .按位异或 (1)格式:x^y (2)规则:对应位相同时为0,不同时为1:3^9=10。 4.按位取反──~ .按位取反 (1)格式:~x (2)规则:各位翻转,即原来为1的位变成0,原来为0的位变成1。 5.按位左移──<< .按位左移 (1)格式:x<< 位数 (2)规则:使操作数的各位左移,低位补0,高位溢出:5<<2=20。
1、有以下程序 、 #include<stdio.h> main( ) {int a=1,b=2,c=3,x; x=(a^b)&c; printf("%d\n", x); } 程序的运行结果是 A)0 B)1 C)2 D)3
2008年4月
1、变量a中的数据用二进制表示的形式是 、变量 中的数据用二进制表示的形式是 中的数据用二进制表示的形式是01011101,变量 中的数据 ,变量b中的数据 用二进制表示的形式是11110000,若要求将a的高 位取反,低四位 ,若要求将 的高 位取反, 的高4位取反 用二进制表示的形式是 不变, 不变,所要执行的运算是 A)a^b B)a|b C)a&b D)a<<4
第11章_位运算

【例11.7】. 阅读程序,写出程序执行结果: main() { char a=64,b=51; printf(“a|b=%0x\n”,a|b); } 程序执行结果显示为: a|b=69 思考一下:a||b的结果为多少。
9.2.3 按位异或运算
按位异或运算是一种双目运算,有两个操作数。运 算符为^。对于一位二进制的按位异或运算,其规 则如下:
第九章 位运算
位运算概述 位运算使用方法 位运算使用举例 位域
9.1 位运算概述
计算机中数的表示
原码 反码 补码
硬件接口寄存器位的读取
9.1.1 计算机中数的表示
原码 反码 补码
原码
表示数的最高位用来表示数的符号,称为号 位。当符号位为1时,表示负数;当符号位 为0时,表示正数。其他位表示数。
求补码的步骤
(1)写出该数的原码; (2)除符号位外,按位取反(即如果原码是1, 则变为0;如果原码为0,则变为1) (3)末位加1。
9.1.2 硬件接口寄存器位的读取与设 置
1=Insert键状态已改变
1=按下右移键(Right Shift)
1=CapsLock键状态已改变
1=NumLock键状态已改变 1=ScrollLock键状态已改变
【例11.9】. 阅读程序,写出程序执行结果: main() { char a=-3,b=50; printf(“a^b=%0d\n”,a^b); } 程序执行结果显示为: a^b=-49
9.2.4 按位取反运算
按位异或运算是一种单目运算,只有一个操作数。 运算符为~。对于一位二进制的按位取反运算,其 规则如下:
【例11.5】 阅读程序,写出程序执行结果: main() { char a=-3,b=50; printf(“a&b=%0x\n”,a&b); } 程序执行结果显示为: a&b=30
C语言程序设计基础教程位运算介绍课件

目录
01. 位运算基础 02. 位运算操作符 03. 位运算实例
1
什么是位运算
位运算是一种对二进制数 进行运算的操作
位运算包括与、或、异或、 左移、右移等操作
位运算可以对二进制数进 行快速、高效的操作
位运算在计算机编程中具 有广泛的应用
位运算的基本操作
与运算:两个操作数对 应位都为1,结果才为1
成一个新的数
位运算的应用场景
01
01
加密和解密:利用位运算对数 据进行加密和解密
02
02
程序优化:利用位运算对程序 进行优化,提高运行效率
03
03
硬件控制:利用位运算对硬件 进行控制,如LED灯、开关等
04
04
数据压缩:利用位运算对数据 进行压缩,节省存储空间
2
按位与操作符
01
符号:&
02
功能:将两个操作数的每一位进行与 操作
或运算:两个操作数对 应位只要有一个为1,
结果就为1
异或运算:两个操作数 对应位不同,结果才为
1
取反运算:将操作数的 每一位都取反
左移运算:将操作数向 左移动指定位数,高位
补0
右移运算:将操作数向 右移动指定位数,低位
补0
循环移位运算:将操作 数向左或向右循环移动
指定位数
位段运算:将操作数中 的指定位提取出来,组
复杂位运算实例
异或运算:用于查找数组中的重复元素 移位运算:用于快速计算乘除法
掩码运算:用于提取数据的特定位 组合运算:用于实现复杂的逻辑判断和操作
位运算在实际编程中的应用
加密和解密:使用位运算对数据进行加密和 解密,提高数据安全性
C语言程序设计课件位运算

使用位异或运算可以检测一个整数的奇偶性。例如,将一 个整数与1进行位异或运算,如果结果为1,则该整数为奇 数;如果结果为0,则该整数为偶数。
示例三
使用位异或运算可以实现快速取模运算。例如,将一个整 数与一个常数进行位异或运算,可以得到该整数除以该常 数的余数。
06 位非运算
位非运算的定义
• 位非运算是一种位运算操作,它对一个二进制数的每一位进行取反操作。如果某一位是1,则位非运算后变为0;如果某一 位是0,则位非运算后变为1。
部分数组交换等。
THANKS FOR WATCHING
感谢您的观看
右移位运算
总结词
将二进制位向右移动若干位。
详细描述
右移位运算符(>>)将一个数的二进制位向右移动若干位,左侧空出的位用符号位填充。对于有符号整数,右 侧空出的位用符号位填充;对于无符号整数,右侧空出的位用0填充。右移位运算实质上是将该数除以2的若干次 方。
无符号右移位运算
总结词
将二进制位向右移动若干位,左侧空出的位用0填充。
C语言程序设计课件位运算
目录
• 位运算概述 • 移位运算 • 位与运算 • 位或运算 • 位异或运算 • 位非运算
01 位运算概述
位运算的定义
01
位运算是一种以二进制位为对象 的运算方式,通过对二进制位进 行操作来执行特定的功能。
02
在C语言中,位运算符包括按位与 (&)、按位或(|)、按位异或(^)、 按位取反(~)、左移(<<)和右移 (>>)。
位非运算的规则
位非运算的规则是将一个二进制数的 每一位都进行取反操作。具体来说, 如果某一位是1,则位非运算后变为0; 如果某一位是0,则位非运算后变为1。
十二章位运算

存储单元分配:共4个字节
ab c
i
23 4
7
16
位段的引用: 结构体变量名. 位段成员名
如:struct packed_data 位段的引用如下:
{ unsigned a : 2; unsigned b : 3; unsigned c : 4;
data.a=2; data.b=7;
int i;
例如:char a=9, b=020; printf(“%o\n”,~a&b<<1);
输出结果:40
§12.3 位段
位段:在一个结构体中可以以位为单位来指定 其成员所占内存长度,这种以位为单位 的成员称为位段(或位域)。
如:struct packed_data
{ unsigned a : 2; unsigned b : 3; unsigned c : 4; int i;
0^0=0 0^1=1 1^0=1 1^1=0
0 | 0=0 0 | 1=1 1 | 0=1 1 | 1=1
例如:main()
{ unsigned char a,b; a=0x9d; b=0xa5; printf(“~a: %x\n”, ~a); printf(“a&b: %x\n”,a&b); printf(“a | b: %x\n”,a | b); printf(“a^b: %x\n”,a^b);
data.c=9;
}data;
注意:位段允许的最大值范围。
data.a=9; ╳
说明:
⑴位段成员的类型必须指定为unsigned int类型。
⑵允许在位段中定义无名字段,其含义为跳过该
字节剩余的位或指定的位不用。当无名字长度
第十一章 位运算

第十一章位运算所谓的位运算是指对数据进行二进制位的运算(按位运算)。
§11.1位运算符和位运算位运算符及优先级:~(按位取反)单目运算符<<(左移),>>(右移)在算术运算符之后,关系运算符之前&(位与)高∧(异或) 在关系运算符之后,逻辑运算符之前|(位或)低结合性:~(按位取反)为从右到左,其余为从左到右。
注意:运算量只能是整型或字符型的数据。
一.按位与运算符“&”运算符“&”先把两个运算对象按位对齐,再进行按位与运算,如果两个对应的位都为1,则该位的运算结果为1,否则为0。
如:3&5= 1 9&11=90000 0011 (十进制数3) 0000 1001 (十进制数9)& 0000 0101 (十进制数5) & 0000 1011 (十进制数11)0000 0001 (十进制数1) 0000 1001 (十进制数9) 应用:1.清零:将一个数中的某几位清为0。
如:0010 1011 (十进制数43)& 1111 0001 (十进制数241)0010 0001 (十进制数33) 即 43 & 241=332.取一个数中的某几位(保留某几位)如有一个整数a(2字节),要得到其低8位,则:a & 0377,要得到其高8位,则:a & 0177400a 0010 1100 1010 1100 a 0010 1100 1010 11000377 0000 0000 1111 1111 0177400 1111 1111 0000 0000二.按位或运算符“|”按位或运算符“|”先把两个运算对象按位对齐,再进行按位或运算,如果两个对应的位都为0,则该位的运算结果为0,否则为1。
如:060|017=077 060 0011 0000017 0000 1111| 0011 1111应用:对某些位置为1。
c语言位运算详解

图11.2 15右移3位得到1
右移1位相当于该数除以2,右移n位相当于该数除以2 n , 因此,将15右移3位,相当于15/2 3 = 1(C语言规定整数相 除商为整数)。
右移时应注意符号问题。对于无符号数,右移时左端补0。 对于有符号数,若符号位为0(该数为正),则右移时左端 补0,同无符号数的处理。若符号位为1(该数为负),则右 移时左端是补0还是补1,取决于所用的计算机系统。有的系 统左端补0,称逻辑右移;左端补1,称算术右移。显然,两 种方式所得的结果是不一样的。Turbo C采用的是算术右移。
scanf (”%u” , & a ) ;
b=a >> 4 ; b=b & 0x000F; printf (” \na=%u b=%u ”, a , b ) ; } 运行情况如下: 115
a=115,b=7
例 2 循环移位。要求将一个无符号数进行左循环移位。如 图11. 4所示。将a左移1位,并将移出位补到右端,输入′e ′结束。
(4)重复以上步骤,直到有键按下。程序如下:
# include <stdio. h> main( ) { unsigned a ; int flag ; scanf (”%u” , & a ) ; while (getchar( ) !=’ e ’ )
{
flag=a & 0x8000 ; a=a<<1 ;
关于位段数据,注意以下几点: (1)一个位段必须存储在同一存储单元(即字)之中,不能跨两 个单元。如果其单元空间不够,则剩余空间不用,从下一个 单元起存放该位段。 (2)可以通过定义长度为0的位段的方式使下一位段从下一存 储单元开始。 (3)可以定义无名位段。 (4)位段的长度不能大于存储单元的长度。
位运算讲解

表12.3
ห้องสมุดไป่ตู้
位运算符的运算规则
运算符 & | ∧
名称 位与 位或 位异或
运算规则 两个相应位全1为1,其余为0 两个相应位全0为0,其余为1 两个相应位相同为0,其余为1
~
<< >>
取反
左移 右移
把操作数各位0变1,1变0
把操作数左移指定的位数 把操作数右移指定的位数
12.1.7 位运算的复合赋值运算符
0001 0010 0011 0100
& b 0000 0000 1111 1111 c 0000 0000 0011 0100
按位与常用于将某个运算量的某些位清0或提取某些位的值, 如本例中,将变量的高8位清0,同时保留或提取低8位的值,可 用一个高8位为0,低8位为1的值0x00ff和它进行按位与。
共有五种位运算的复合赋值运算符:&=(位 与赋值)、|=(位或赋值) 、∧= (位异或赋值 ) 、>>=(按位右移赋值)、<<= (按位左移赋 值)。
例如:a ∧=b a>>=3 相当于a= a ∧b 相当于a= a>>3
12.2 位段的概念
所谓位段,相当于结构体类型中的
成员,但是定义位段的长度是以位为单
12.1 位运算
C语言具有位逻辑运算和移位运算的 独特功能,参与位运算的运算量必须是整 数。位逻辑运算包括&、|、∧、~四种运 算,移位运算有<<、>>两种运算。其中~ 位单目运算符。
12.1.1 12.1.2 12.1.3 12.1.4 12.1.5 12.1.6 12.1.7
提高组班讲座位运算及其应用ppt课件

例题三:起床困难综合症(sleep.*,1秒, 256M) 算法的时间复杂度是多少? O(n)的实现 一开始用0000..00和1111..11计算
出每位初始时为0或1的结果
位运算的优化!
例题四:还是N皇后
问题描述: 正如题目所说,这题是著名的N皇后问题。 输入格式: 第一行有一个N。接下来有N行N列描述一个棋盘,
“*”表示可放“.”表示不可放。 输出格式: **.* **** **** **** 输出样例: 1 补充说明: 对于30%的数据,N≤10; 对于100%的数据,N≤14;
例题四:还是N皇后
暴力?搜索? 裸的DFS必然TLE?
满足贪心的性质
例题三:起床困难综合症(sleep.*,1秒, 256M) 算法的时间复杂度是多少?
例题三:起床困难综合症(sleep.*,1秒, 256M) 算法的时间复杂度是多少? O(nlogn)的实现
例题三:起床困难综合症(sleep.*,1秒, 256M) 算法的时间复杂度是多少? O(n)的实现
例题三:起床困难综合症(sleep.*,1秒, 256M)
输入样例: 3 10 AND 5 OR 6 XOR 7 输出样例: 1 样例说明: atm可以选择的初始攻击 力为0,1,…,10。 假设初始攻击力为4,最 终攻击力经过了如下计算
4 AND 5 = 4 4 OR 6 = 6 6 XOR 7 = 1 类似的,我们可以计算出初 始攻击力为1,3,5,7,9时最终攻 击力为0,初始攻击力为 0,2,4,6,8,10时最终攻击力为1, 因此atm的一次攻击最多使 drd 受到的伤害值为1。 2<=m<=10^9,0<=t<=10^9, 一定为OR,XOR,AND 中的一 种
第八章 位运算

HFUT School of Computer Xuan & li
格式:
a >> n 操作: 无符号数称为逻辑右移,低位移出,高位补0; 有符号数称为算数右移,低位移出,符号位自补。 右移一位相当于除 2。
例:通过移位实现循环移位
⒉移位运算赋值
HFUT School of Computer Xuan & li
运算符 <<=
>>=
示例 a <<= 7
s >> = (n+3)
HFUT School of Computer Xuan & li
⒉位段(位域)的定义 格式: struct 位段类型 { type 位段名1: 长度1; type 位段名2: 长度2; …… type 位段名n: 长度n; }; struct bitfield1 { unsigned int a:4 ; unsigned int b:4 ; unsigned int c:2 ; }
HFUT School of Computer Xuan & li
/* 占4位 */ /* 占4位 */ /* 占2位 */
struct bitfield1 x; //说明变量 x.a = 12;
位段(位域)使用实例 #include<stdio.h> void main(void) { struct exam1 { short int a:4 ; short int b:4 ; short int c:2 ; } x; x.a = 7; x.b = 2; x.c = 1; printf(" %d %d %d\n", x.a,x.b,x.c); }
第A章-位运算

运算符 >> --- 右移
格式: 格式 a >> n 作用: 将位串 的各个位向右移 的各个位向右移n位 右移后右端的位被移出丢失. 作用 将位串a的各个位向右移 位, 右移后右端的位被移出丢失 左端有两种补法: 或 因此, 分成两种移法: 左端有两种补法 0或1, 因此 分成两种移法 逻辑右移: 左端总是补0 逻辑右移 左端总是补 如, a=(1100 0010 1011 0110)2 ,则 a >>1 后变成: 则 后变成 0110 0001 0101 1011 0 如果a是负整数 是负整数, 如果 是负整数 逻辑右移 丢失 后就会变成正整数 !
运算符 ~ --- 按位取反 单目、右结合 按位取反(单目 右结合) 单目、
运算规则: 运算规则 ~0 = 1; ~1 = 0; 注意与逻辑非( 的区别 的区别: 注意与逻辑非 ! )的区别 设 int a=5; 则 !a (1111 1111 1111 1010)2 而 ~a 0;
将整数a的第 位变成0, 其它位不变. 的第0位变成 例: 将整数 的第 位变成 其它位不变 字节, 字节, 若a是2字节 可用 a & 0xfffe; 若a是4字节 可用 a & 0xfffffffe; 是 字节 是 字节 这样做不利于程序移植, 这样做不利于程序移植 可用 a & (~1); 对于16位系统 相当于a 位系统, 位系统, 对于 位系统 相当于 & 0xfffe; 32位系统 相当于 & 0xfffffffe; 位系统 相当于a
0 0 1 1 0 1 0 1 0 1 1 0 0 0 1 1 0 1 0 1
运算结果使p, 的值交换 的值交换. 运算结果使 q的值交换
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
位运算由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。
当然有人会说,这个快了有什么用,计算6 and 11没有什么实际意义啊。
这一系列的文章就将告诉你,位运算到底可以干什么,有些什么经典应用,以及如何用位运算优化你的程序。
Pascal和C中的位运算符号下面的a和b都是整数类型,则:C语言| Pascal语言-------+-------------a&b| a and ba|b| a or ba ^ b| a xor b~a| not aa << b| a shl ba >>b | a shr b注意C中的逻辑运算和位运算符号是不同的。
520|1314=1834,但520||1314=1,因为逻辑运算时520和1314都相当于True。
同样的,!a和~a也是有区别的。
各种位运算的使用=== 1. and运算===and运算通常用于二进制取位操作,例如一个数and 1的结果就是取二进制的最末位。
这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数.相同位的两个数字都为1,则为1;若有一个不为1,则为0。
0011111100(&或者and)----------------00100=== 2. or运算===or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。
如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。
相同位只要一个为1即为1。
0011111100(|或者or)----------------11111=== 3. xor运算===异或的符号是⊕。
xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。
xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。
1314520 xor 19880516 =20665500,我就把20665500告诉MM。
MM再次计算20665500xor 19880516的值,得到1314520,于是她就明白了我的企图。
相同位不同则为1,相同则为0。
0011111100(^或者xor)----------------11011运算结果x <- x # yy <- x @ yx <- x @ y执行了第一句后x变成了x #y。
那么第二句实质就是y <- x # y@ y,由于#和@互为逆运算,那么此时的y变成了原来的x。
第三句中x实际上被赋值为(x # y) @ x,如果#运算具有交换律,那么赋值后x就变成最初的y了。
这三句话的结果是,x和y的位置互换了。
加法和减法互为逆运算,并且加法满足交换律。
把#换成+,把@换成-,我们可以写出一个不需要临时变量的swap过程(Pascal)。
procedure swap(vara,b:longint);begina:=a + b;b:=a - b;a:=a - b;end;好了,刚才不是说xor的逆运算是它本身吗?于是我们就有了一个看起来非常诡异的swap过程:procedure swap(vara,b:longint);begina:=a xor b;b:=a xor b;a:=a xor b;end;注意:位运算版本的交换两数不适用于一个数的自我交换。
也就是说,如果上述程序的“b”改成“a”的话,其结果是变量a变成零。
因此,在使用快速排序时,由于涉及到一个数的自我交换,因此如果要在其中使用位运算版的交换两数的话,应该先判断。
具体的时间损耗在此略过。
=== 4. not运算===not运算的定义是把内存中的0和1全部取反。
使用not运算时要格外小心,你需要注意整数类型有没有符号。
如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用00到$FFFF依次表示的。
下面的两个程序(仅语言不同)均返回65435。
vara:word;begina:=100;a:=not a;writeln(a);end.#include <stdio.h>int main(){unsigned short a=100;a = ~a;printf( "%d\n", a );return 0;}如果not的对象是有符号的整数,情况就不一样了,稍后我们会在“整数类型的储存”小节中提到。
=== 5. shl运算===a shl b就表示把a转为二进制后左移b位(在后面添b个0)。
例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。
可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。
因此程序中乘以2的操作请尽量用左移一位来代替。
定义一些常量可能会用到shl运算。
你可以方便地用1 shl 16 - 1来表示65535。
很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。
=== 6. shr运算===和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。
我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。
想办法用shr代替除法运算可以使程序效率大大提高。
最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。
位运算的简单应用有时我们的程序需要一个规模不大的Hash表来记录状态。
比如,做数独时我们需要27个Hash表来统计每一行、每一列和每一个小九宫格里已经有哪些数了。
此时,我们可以用27个小于2^9的整数进行记录。
例如,一个只填了2和5的小九宫格就用数字18表示(二进制为000010010),而某一行的状态为511则表示这一行已经填满。
需要改变状态时我们不需要把这个数转成二进制修改后再转回去,而是直接进行位操作。
在搜索时,把状态表示成整数可以更好地进行判重等操作。
这道题是在搜索中使用位运算加速的经典例子。
以后我们会看到更多的例子。
下面列举了一些常见的二进制位的变换操作。
功能| 示例| 位运算----------------------+---------------------------+--------------------去掉最后一位| (101101->10110) | x shr 1在最后加一个0| (101101->1011010) | x shl 1在最后加一个1| (101101->1011011) | x shl 1+1把最后一位变成1| (101100->101101) | x or 1把最后一位变成0| (101101->101100) | x or 1-1最后一位取反| (101101->101100) | x xor 1把右数第k位变成1 | (101001->101101,k=3) | x or (1 shl (k-1))把右数第k位变成0 | (101101->101001,k=3) | x and not (1 shl (k-1))右数第k位取反| (101001->101101,k=3) | x xor (1 shl (k-1))取末三位| (1101101->101) | x and 7取末k位| (1101101->1101,k=5) | x and (1 shl k-1)取右数第k位| (1101101->1,k=4) | x shr (k-1) and 1把末k位变成1| (101001->101111,k=4) | x or (1 shl k-1)末k位取反| (101001->100110,k=4) | x xor (1 shl k-1)把右边连续的1变成0 | (100101111->100100000) | x and (x+1)把右起第一个0变成1 | (100101111->100111111) | x or (x+1)把右边连续的0变成1 | (11011000->11011111) | x or (x-1)取右边连续的1 | (100101111->1111) | (x xor (x+1)) shr 1去掉右起第一个1的左边| (100101000->1000) | x and (x xor (x-1))最后这一个在树状数组中会用到。
Pascal和C中的16进制表示Pascal中需要在16进制数前加$符号表示,C中需要在前面加0x来表示。
这个以后我们会经常用到。
整数类型的储存我们前面所说的位运算都没有涉及负数,都假设这些运算是在unsigned/word类型(只能表示正数的整型)上进行操作。
但计算机如何处理有正负符号的整数类型呢?下面两个程序都是考察16位整数的储存方式(只是语言不同)。
#include <stdio.h> int main(){short int a, b;a = 0x0000;b = 0x0001;printf( "%d %d ", a,b );a = 0xFFFE;b = 0xFFFF;printf( "%d %d ", a,b );a = 0x7FFF;b = 0x8000;printf( "%d %d\n",a, b );return 0;}两个程序的输出均为0 1 -2 -1 32767 -32768。
其中前两个数是内存值最小的时候,中间两个数则是内存值最大的时候,最后输出的两个数是正数与负数的分界处。
由此你可以清楚地看到计算机是如何储存一个整数的:计算机用00到$7FFF依次表示0到32767的数,剩下的$8000到$FFFF依次表示-32768到-1的数。
32位有符号整数的储存方式也是类似的。
稍加注意你会发现,二进制的第一位是用来表示正负号的,0表示正,1表示负。
这里有一个问题:0本来既不是正数,也不是负数,但它占用了00的位置,因此有符号的整数类型范围中正数个数比负数少一个。
对一个有符号的数进行not运算后,最高位的变化将导致正负颠倒,并且数的绝对值会差1。