• C++反汇编学习笔记(一)


    Chinese:

    C++整数与浮点数的存储方式

    1、整数类型

    无符号整数的所有位都用来表示数值,有符号整数的最高位是符号位,其余位以补码格式存储。补码规则就是用0减去这个数的绝对值(取反加一)。首先我们知道x+x(反码)=0xFFFFFFFF, 因此x+x(反码)+1=0, 因此x(补码)=0-x=x(反码)+1。补码的好处就是方便计算机做加法。

    2、浮点数类型

    C++中的浮点数类型分为float和double,float占4个字节、double占8个字节。浮点数的操作不会用到通用寄存器,而是使用浮点协处理器的浮点寄存器。

    浮点数的编码方式采用的是IEEE规定的编码标准,float和double两种类型的数据的转换原理相同,不同的只是表示范围。IEEE规定的浮点数编码会将一个浮点数转换成二进制数。以科学计数法划分,将浮点数拆分为3个部分:符号、指数、尾数。

    float占4个字节,最高位是符号位,紧接着的8位表示指数,其余位表示尾数。

    实例分析:

    (1) 12.25f 转换成二进制是1100.01,1100.01 = 1.10001 * 2 ^ 3。由于在二进制中,尾数的最高位始终是1,所以忽略不计。因此尾数位是1000100000...剩下添0。指数部分有一个规定,由于指数可能出现负数,十进制的127表示为二进制为01111111。IEEE编码方式规定,当指数域小于01111111时为一个负数,反之为正数。因此01111111为0,所以指数位为3+127=130,二进制为10000010。

    符号位:0;指数位:10000010;尾数位:10000100000000000000000;表示成16进制为0x41440000

    (2) -0.125f转换成二进制是-0.001,由于是负数所以符号位是1, 0.001=1.0 * 2 ^ -3,尾数最高位始终为1,所以忽略不计。尾数位是00000....,指数位是-3+127=124,二进制是1111100。

    符号位:1;指数位:01111100;尾数位:00000000000000000000000;表示成16进制为0xBE000000

    (3)1.3f转换成二进制的时候,由于0.3转换成二进制会得到一个无穷值: 0.3=0.25+0.03175+....,所以尾数位会舍弃多余的部分。转换成二进制就约等于1.01001100110011001100110,到第23位终止。指数位为0+127,二进制为01111111

    符号位:0;指数位:01111111;尾数位:01001100110011001100110;表示成16进制为0x3FA66666

     

    所以我们得出IEEE编码转换后,得到的是一个近似值,存在一定的误差。所以这也就解释了为何在c++判断浮点数值是否为0时,要做一个区间比较而不是直接进行等值比较。

    float fNum;
    float fScope = 0.0001f;
    if (fNum >= -fScope && fNum <= fScope)
    {
            // fNum等于0
    }

    3、double类型的IEEE编码

    double类型和float类型大同小异,只是double类型表示的范围更大,精度更高。最高位是符号位,指数位占11位,尾数位占42位。double扩大了精度,因此指数位需要加上1023,这些都可以推断出来,这里便不再赘述。

     

    4、浮点数指令

    浮点数的操作指令和普通数据类型不同,浮点数操作是通过浮点寄存器实现的,而普通数据类型是通过通用寄存器,它们使用的是两套不同的指令。

    浮点寄存器是通过栈结构来实现的,由ST(0)~ST(7)一共8个栈空间组成,每个浮点寄存器占8个字节。每次使用浮点寄存器都是率先压入ST(0),不能越过ST(0)直接使用ST(1),当8个寄存器都有数据时,此时再压入数据的时候,ST(7)的值会被丢弃。

    整型数据与浮点型数据之间的相互转换,来看一下反汇编出来的指令。

    // C++ Code:
    float fNum = (float)argc;
    
    ;将地址ebp+8处的整型数据转换成浮点型,并放入ST(0)中,对应变量argc
    fild dword ptr [ebp+8]
    ;从ST(0)中取出数据以浮点编码方式存入地址ebp-4中,对应变量fNum
    fst dword ptr [ebp-4]
    
    // C++ Code:
    printf("%f", fNum);
    
    ;
    这里对esp执行减8操作是由于浮点数作为变参函数的参数时需要转换为双精度浮点值 ;这步操作是提前准备8字节的栈空间,以便存放double数据 sub esp,8 ;将ST(0)中的数据传入esp中,并弹出ST(0) fstp qword ptr [esp] ;以下为printf函数调用,略 push offset string "%f" (0042302c) call printf (0040e940) add esp, 0ch // C++ Code: argc = (int)fNum;
    ;
    将地址ebp-4处的数据以浮点型压入ST(0)中 fld dword ptr [ebp-4] ;调用函数__ftol进行浮点数转换 call __ftol (0040e688) ;转换后结果放入eax,并传递到ebp+8地址处 mov dword ptr[ [ebp+8], eax // C++ Code: printf("%d", argc); ;略。。。

    浮点数虽然占4个字节,但都是以8个字节的方式进行处理。当浮点数作为参数时,不能直接压栈。push指令只能传入4字节的数据到栈中,这样会丢失4字节的数据。所以使用printf以整数方式输出浮点数时会产生错误。

     

    浮点数作为返回值的汇编指令:

    // C++ Code:
    fNum = GetFloat();
    
    ;调用函数GetFloat
    call @ILT+5 (GetFloat)  (0040100a)
    ;由于浮点数需要特殊处理,浮点数占8字节,无法使用eax进行传递
    ;使用浮点寄存器ST(0)作为返回值
    fst dword ptr [ebp-4]
    
    // C++ Code:
    float GetFloat()
    {
        return 12.25f;
    }
    
    ;将浮点数保存在ST(0)中,在返回值为浮点数的情况下,无法使用eax
    ;使用ST(0)作为返回值进行传递
    fld dword ptr [__real@4@4002c400000000000000  (0042301c)]
    ret
  • 相关阅读:
    JSTL fn:split()函数
    JSTL判断list是否为空
    oracle 合并列的函数wm_concat
    Eclipse软件使用说明
    Caused by: org.hibernate.HibernateException: identifier of an instance of ... is alterde from
    SpringData JPA详解
    关于JPA方法名创建自动查询
    jquery移除、绑定、触发元素事件使用示例详解
    js String对象中常用方法小结(字符串操作)
    java中list的使用方法
  • 原文地址:https://www.cnblogs.com/maplewan/p/3232645.html
Copyright © 2020-2023  润新知