一、为什么要位运算
- 位运算的效率很高
- 因为计算机处理的都是一些二进制数,计算机不懂我们所说的加减乘除,这些所有的运算,计算机最后都会使用位运算得到结果
- 位运算还可以用来加密
二、汇编中的移位指令
1.算数移位指令
-
SAL(Shift Arithmetic Left):算术左移(和SHL效果一样)
-
SAR(Shift Arithmetic Right):算术右移
-
格式:
SAL/SAR Reg/Mem, CL/Imm
即算数移位指令后面的第一个操作数是寄存器或者内存;第二个操作数是寄存器或者立即数
SAL eax,2 SAL ax,1 SAL al,3
-
怎么实现:
-
如果
SAL eax,1
,表示将eax中的数(4字节)左移1位,最高位的数移入到CF进位标志位中,最低位补0 -
如果
SAR ax,1
,表示将ax中的数(2字节)右移1位,最高位的补原来的ax中的符号位(原来的最高位),最低位移入到CF进位标志位中举例: SAR al,1 //al:10000001右移一位最高位补原来符号位,最低位移入CF,即11000000 / CF:1 SAL al,2 //al:10000001左移两位最高位移入CF,最低位补0,即00000100 / CF:0
-
如果是移动位数大于1,就一个一个依次按照移动1位这样做
-
-
特殊:
SAL al,8 //表示将al寄存器中8位全部变成0 SAR ax,0x10 //表示将ax寄存器中16位全部原来最高位的符号位
为什么
sar/sal eax,0x20
有点问题
2.逻辑移位指令
-
SHL(Shift Left):逻辑左移
-
SHR(Shift Right):逻辑右移
-
格式:
SHL/SHR Reg/Mem, CL/Imm
SHL eax,2 SHR word ptr es:[ebp],1 SHL al,1
-
怎么实现:
-
如果
SHL eax,1
,表示将eax中的数(4字节)左移1位,最高位的数移入到CF进位标志位中,最低位补0 -
如果
SHR al,1
,表示将al中的数(1字节)右移1位,最高位补0,最低位移入到CF进位标志位中举例: SHR al,1 //al:10000001右移一位最高位补0,最低位移入CF,即01000000 / CF:1 SHL al,1 //al:01000001左移一位最高位移入CF,最低位补0,即10000010 / CF:0
-
3.循环移位指令
-
ROL(Rotate Left):循环左移
-
ROR(Rotate Right):循环右移
-
格式:
ROL/ROR r/m, i8/CL
ROL eax,3 ROL cx,1 ROR cl,2
-
怎么实现:
-
如果
ROL eax,1
,表示将eax中的数循环左移1位,最高位的数补到最低位,并且最高位的数移入CF标志位 -
如果
ROR al,1
,表示将al中的数循环右移1位,最低位的数补到最高位,并且最低位的数移入CF标志位举例 ROL al,1 //al:10000001循环左移一位最高位补到最低位,最高位移入CF,即00000011 / CF:1 ROR al,1 //al:10000001循环右移一位最低位补到最高位,最低位移入CF,即11000000 / CF:1
-
4.带进位的循环移位指令
-
RCL(Rotate through Carry Left):带进位循环左移
-
RCR(Rotate through Carry Right):带进位循环右移
-
格式:
RCL/RCR r/m, i8/CL
RCL eax,1 RCR cx,2 RCR al,1
-
怎么实现:
-
如果
RCL al,1
,表示将al中的数循环左移一位,最高位移入CF标志位,并且CF原来的数补到最低位 -
如果
RCR al,1
,表示将al中的数循环右移一位,最低位移入CF标志位,并且CF原来的数补到最高位举例: RCL al,1 //al:10000001,CF:0循环左移一位最高位移入CF,CF原来的数补到最低位,即al:00000010 / CF:1 RCR al,1 //al:10000001,CF:0循环右移一位最低位移入CF,CF原来的数补到最高位,即al:01000000 / CF:1
-
三、C语言中的移位运算
-
与运算
&
printf("%d",2&3); //2(写成二进制,按位做即可)
-
或运算
|
printf("%d",2|3); //3
-
非运算
~
printf("%d",~2); //-3,因为%d打印的是有符号的整数
-
异或运算
^
printf("%d",2^3); //1
-
移位运算
<<
>>
-
左移运算,有符号和无符号是无区别的
int a = 8; printf("%d",a<<1); //16 unsigned int b = 8; printf("%d",b<<1); //16
查看反汇编:生成的汇编指令都是shl,逻辑左移
-
右移运算,有符号对应SAR即算数右移,无符号对应SHR即逻辑右移
int a = 0xF0000000; printf("%d",a>>1); //-134217728 unsigned int b = 0xF0000000; printf("%d",b>>1); //2013265920
查看反汇编:有符号右移生成的指令为SAR,无符号右移生成的指令为SHR
-
四、作业
-
定义一个unsiged char 类型,通过程序为第3、5、7位赋值,赋值时不能影响到其它位原来的值(使用位操作指令)
#include "stdafx.h" #include "stdlib.h" void Func(unsigned char a){//假设第3,5,7位要赋的值1,0,1 a = a | 0x44; //0100 0100 a = a & 0xef; //1110 1111 printf("%x",a); //0110 0100 } int main(int argc,char* argv){ unsigned char a = 0x34; //0011 0000 Func(a); system("pause"); return 0; }
-
判断某个位的值是否为1
#include "stdafx.h" #include "stdlib.h" void Func(unsigned char a){//假设要判断第三位的值是否是1 if((a & 0x04) == 0){ //0000 0100 这里一定要大括号,因为位运算优先级很低 printf("不是1"); }else{ printf("是1"); } } int main(int argc,char* argv){ unsigned char a = 0x34; //0011 0100 Func(a); system("pause"); return 0; }
-
读取第7、6、5位的值,以十进制显示(unsigned)
#include "stdafx.h" #include "stdlib.h" void Func(unsigned char a){ a = a & 0x70; //0111 0000 a = a >> 4; printf("%d",a); } int main(int argc,char* argv){ unsigned char a = 0x54; //0101 0100 Func(a); system("pause"); return 0; }
-
用十六进制文本编辑器分别打开一个.exe、.dll、.sys、.txt、.doc、.jpg、.pdf等将前两个字节写在下面
- .exe:内存中显式为
4D 5A
,我们读作0x5A4D - .dll:同样是
4D 5A
- .sys:
4D 5A
开头,但也有25 75
- .txt:
0A 66
,读作0x660A - .doc:
D0 CF
,读作0xCFD0 - .jpg:
FF D8
- .pdf:
25 50
,读作0x5025
- .exe:内存中显式为
-
将一个在十六进制编辑器中打开的.exe文件,拖拽到最后,观察文件中的大小和硬盘上的大小
可以发现用十六进制编辑器打开一个.exe文件,它的最大地址0x0829CDFF,换算成10进制等于136957439字节,我们发现该文件是从地址0x00000000开始算的,所以算大小还要加1字节,即136957439+1=136957440字节
小提示:winhexoffset栏单击一下,可以在十进制和十六进制之间转换显示