滴水三期:day25.1-汇编和C的移位指令

一、为什么要位运算

  • 位运算的效率很高
  • 因为计算机处理的都是一些二进制数,计算机不懂我们所说的加减乘除,这些所有的运算,计算机最后都会使用位运算得到结果
  • 位运算还可以用来加密

二、汇编中的移位指令

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,逻辑左移

      屏幕截图 2021-12-18 105543
    • 右移运算,有符号对应SAR即算数右移,无符号对应SHR即逻辑右移

      int a = 0xF0000000;
      printf("%d",a>>1);  //-134217728
      unsigned int b = 0xF0000000;
      printf("%d",b>>1);  //2013265920
      

      查看反汇编:有符号右移生成的指令为SAR,无符号右移生成的指令为SHR

      屏幕截图 2021-12-18 110345

四、作业

  • 定义一个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文件,拖拽到最后,观察文件中的大小和硬盘上的大小

    屏幕截图 2021-12-18 130127

    可以发现用十六进制编辑器打开一个.exe文件,它的最大地址0x0829CDFF,换算成10进制等于136957439字节,我们发现该文件是从地址0x00000000开始算的,所以算大小还要加1字节,即136957439+1=136957440字节

    小提示:winhexoffset栏单击一下,可以在十进制和十六进制之间转换显示

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值