二进制或汇编中如何判断数据的类型(比如是有符号类型还是无符号类型)

众所周知,在二进制代码中的数据是不存在类型这个概念的,我们只能知道某个寄存器或者内存上有一个多少位的数据。那么,计算机是如何来判断某个数据能进行哪些操作的呢?这就是在编译和汇编时需要完成的工作了。通过分析指令或者已知函数操作内存地址的方式,我们可以获得一些信息。

下面看几个例子。

例1

int main(){
        int count = 1000;
        unsigned char loop = 0;
        for (; loop < count; loop++) ;
        return 0;
}

其中涉及到 int 类型和 unsigned char 类型的比较。上面这段代码翻译成汇编语言是:

00000000000005fa <main>:
 5fa:   55                      push   %rbp
 5fb:   48 89 e5                mov    %rsp,%rbp
 5fe:   c7 45 fc e8 03 00 00    movl   $0x3e8,-0x4(%rbp)
 605:   c6 45 fb 00             movb   $0x0,-0x5(%rbp)
 609:   0f b6 45 fb             movzbl -0x5(%rbp),%eax
 60d:   39 45 fc                cmp    %eax,-0x4(%rbp)
 610:   7e 0c                   jle    61e <main+0x24>
 612:   0f b6 45 fb             movzbl -0x5(%rbp),%eax
 616:   83 c0 01                add    $0x1,%eax
 619:   88 45 fb                mov    %al,-0x5(%rbp)
 61c:   eb eb                   jmp    609 <main+0xf>
 61e:   b8 00 00 00 00          mov    $0x0,%eax
 623:   5d                      pop    %rbp
 624:   c3                      retq
 625:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 62c:   00 00 00
 62f:   90                      nop

可以看出使用了 movzbl 指令先从 -0x5(%rbp) 拷贝到 %eaxzbl 后缀表示零扩展从 byte 类型到 long 类型,所以只拷贝了低 8位。对 %eax 加1后再用 mov%al 拷贝回 -0x5(%rbp) ,因为是 %al 所以只有低8位。因此,通过指令把数据限制在了低8位。

例2

我们用下面两段代码的汇编代码来看下,对于同样是32位的有符号整数和无符号整数,计算机是如何区别的。

  • 代码1
#include <stdint.h>
int main(){
        int32_t count = INT32_MAX;
        int32_t loop = 0;
        for (; loop < count; loop++) ;
        return 0;
}

对应

00000000000005fa <main>:
 5fa:   55                      push   %rbp
 5fb:   48 89 e5                mov    %rsp,%rbp
 5fe:   c7 45 fc ff ff ff 7f    movl   $0x7fffffff,-0x4(%rbp)
 605:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
 60c:   8b 45 f8                mov    -0x8(%rbp),%eax
 60f:   3b 45 fc                cmp    -0x4(%rbp),%eax
 612:   7d 06                   jge    61a <main+0x20>
 614:   83 45 f8 01             addl   $0x1,-0x8(%rbp)
 618:   eb f2                   jmp    60c <main+0x12>
 61a:   b8 00 00 00 00          mov    $0x0,%eax
 61f:   5d                      pop    %rbp
 620:   c3                      retq
 621:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 628:   00 00 00
 62b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  • 代码2
#include <stdint.h>
int main(){
        uint32_t count = UINT32_MAX;
        uint32_t loop = 0;
        for (; loop < count; loop++) ;
        return 0;
}

对应

00000000000005fa <main>:
 5fa:   55                      push   %rbp
 5fb:   48 89 e5                mov    %rsp,%rbp
 5fe:   c7 45 fc ff ff ff ff    movl   $0xffffffff,-0x4(%rbp)
 605:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
 60c:   8b 45 f8                mov    -0x8(%rbp),%eax
 60f:   3b 45 fc                cmp    -0x4(%rbp),%eax
 612:   73 06                   jae    61a <main+0x20>
 614:   83 45 f8 01             addl   $0x1,-0x8(%rbp)
 618:   eb f2                   jmp    60c <main+0x12>
 61a:   b8 00 00 00 00          mov    $0x0,%eax
 61f:   5d                      pop    %rbp
 620:   c3                      retq
 621:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 628:   00 00 00
 62b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

从两段代码我们可以看出主要的区别就只有 612 这个地址上的内容。对于有符号数,比较时用的是 jge ,而无符号数使用 jae 。这样的原因是计算机无需数据类型,只要指令使用得正确即可执行。因此可以知道,单独从数据来看,是无法得到其类型的,我们只有通过指令操作来推断出类型。

例3

补充一个例子,和前面例2的代码2可以一起看。

#include <stdint.h>
int main(){
        uint32_t count = UINT32_MAX;
        int32_t loop = 0;
        for (; loop < count; loop++) ;
        return 0;
}

对应

00000000000005fa <main>:
 5fa:   55                      push   %rbp
 5fb:   48 89 e5                mov    %rsp,%rbp
 5fe:   c7 45 fc ff ff ff ff    movl   $0xffffffff,-0x4(%rbp)
 605:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
 60c:   8b 45 f8                mov    -0x8(%rbp),%eax
 60f:   39 45 fc                cmp    %eax,-0x4(%rbp)
 612:   76 06                   jbe    61a <main+0x20>
 614:   83 45 f8 01             addl   $0x1,-0x8(%rbp)
 618:   eb f2                   jmp    60c <main+0x12>
 61a:   b8 00 00 00 00          mov    $0x0,%eax
 61f:   5d                      pop    %rbp
 620:   c3                      retq
 621:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 628:   00 00 00
 62b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

分析两段汇编代码,其实含义上没有任何区别。对于例2中的代码2, cmp count, loop 时,用的是 jae 指令, loop 无符号大于等于 count 则跳转退出;对于后面这个代码, cmp loop, count 时,用的是 jbe 指令, count 无符号小于等于 loop 则跳转退出。两个意思是一样的。这说明在后面的这个例子中,执行到 loop < count 时,有符号类型变量 int32_t loop 会被强转成无符号类型。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值