【数值溢出】从二进制的角度看数值溢出

编程语言中的数字数据类型都预设了大小,也就是说,一个数字数据类型的变量,总会有能表达的上限,有上限就会有溢出。本篇从二进制的底层,分析解释一下数值溢出问题。以byte为例。


0x01.问题引入

  • 看如下一段Java代码,你能立马说出输出结果吗?
public class Main {
    public static void main(String[] args) {
        int a=1888;
        byte b=(byte) a;
        System.out.println(b);
    }
}
  • 或许你只能意识到:反正不是1888,反正不会超过byte能表示的数据范围。
  • 赶紧把代码复制下来编译运行一下,一看输出结果:96
  • 为什么会是96呢?
  • 清楚这个之前,我们不妨去探究一下,底层都干了些什么。

0x02.byte能表示的数据范围

  • 我们都知道,byte能表示的数据范围是:-128-127,但是,为什么是这个范围呢?

  • 原因:在Java中,对于byte类型的变量,JVM会为其分配一个字节的内存,一个字节也就是8位,但是由于最高位是符号位,所以能够表示的数据范围就是 -2^7 --2^7-1。原码,反码,补码知识补充:

    • 原码:最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。
    • 反码:正数的反码与其原码相同;负数的反码是对其原码逐位取反,符号位除外。
    • 补码:正数的补码与其原码相同;负数的补码是在其反码的末位加1(负数的补码是其绝对值取反)。
    • 在计算机中,用补码表示二进制数。
  • 范围可视化:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 其它数值数据类型的范围,跟byte范围的计算,一模一样。

0x03.byte的溢出原理

  • 回到最初的例子,我们看一下那个转换的过程都发生了什么。
  • int类型变量占4个字节的内存,也就是32位,所以它能表示的数据范围是:-2^31--2^31-1
  • 那么a变量在内存中的表示,是这样吗?

在这里插入图片描述

  • 必然不是,因为上面说到,一个int类型的变量,占了32位,那么完整的形式应该是:

在这里插入图片描述

  • 而此时一个未初始化的byte变量b在内存中应该是:

在这里插入图片描述

  • 此时如果进行强制类型转换 b=(byte)a,int类型变量的低8位会给byte,然后int其它位的数据就会丢失。如下:

在这里插入图片描述

  • 这也正好解释了最初b的输出问题。

0x04.快速计算溢出后的值

  • 使用二进制去理解原理固然清晰,但是如果都转到二进制计算的话,肯定是会比较复杂的。
  • 我们不如看下它的转换规律:
int a=128;
byte b=(byte) a;
System.out.println(b);
//上述代码输出-128

int a=129;
byte b=(byte) a;
System.out.println(b);
//上述代码输出-127

int a=383;
byte b=(byte) a;
System.out.println(b);
//上述代码输出127

int a=384;
byte b=(byte) a;
System.out.println(b);
//上述代码输出-128

int a=-129;
 byte b=(byte) a;
System.out.println(b);
//上述代码输出127

  • 不难发现,随着int的增大,byte转换后的值都在一个循环内。如下:

在这里插入图片描述

  • 可以这样看这个图,在byte范围内127+1=-128-128-1=127

  • 那么整个的计算可以总结为:

    • 若a>127,上界溢出,顺时针循环,那么强转成byte后,若以0为起点,对256取余得到的值,就是在这个圈中从0开始往右走了多少。例如:1888,对256取余得96,相当于从0向右走了96,所以最后的值就是96。1920,对256取余得128,相当于从0向右走了128,所以最后的值就是-128。

    • 若a<-128,下界溢出,逆时针循环,那么强转成byte后,若以0为起点,对256取余得到的值,就是在这个圈中从0开始往左走了多少。例如:-385,对256取余得129,相当于从0开始向左走了129,所以最后的值就是127。依次类推。

  • 只要记住循环圆,计算还是比较简单的。

0x05.推广到所有数值类型的溢出

  • byte的溢出是一个数值溢出的典型例子,其它数值类型因为表示的范围比较大,发生溢出的情况比较少,不过其所有溢出的原理和byte类型一模一样。
  • int类型溢出的例子:
public class Main {
    public static void main(String[] args) {
        System.out.println(Integer.MAX_VALUE+1==Integer.MIN_VALUE);
        //输出true
    }
}
  • 数值溢出也算是一个很经典的漏洞,如果有数值溢出点,经过攻击者精心的构造利用,可能会造成巨大的损失。具体数值溢出漏洞利用,可以参考:整型溢出漏洞

随时注意数据类型范围很重要哦~~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ATFWUS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值