运算产生溢出
一般我们的整数运算都是基于有限域上的运算,所以会产生溢出,溢出的结果可能不是我们所预期的,那么计算机在设计的时候,对于这种运算为什么不发生警告,
访问野指针,原理上是可行的,但不是我们所预期的,系统会发出警告
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
一般我们的整数运算都是基于有限域上的运算,所以会产生溢出,溢出的结果可能不是我们所预期的,那么计算机在设计的时候,对于这种运算为什么不发生警告,
访问野指针,原理上是可行的,但不是我们所预期的,系统会发出警告
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(3)
1, 整数分为有符号数和无符号数, 无符号数产生回绕, 有符号数产生溢出.
回绕是指, 无符号数,
0-1
会变成最大的数, 如1字节的无符号数会变成255
. 而255+1
会变成最小数0
. 这里的255
只是1字节的最大值, 对于2字节为65535
. 关于C
语言中, 有无符号通过unsigned
声明. 但在Java
等虚拟机语言是没有无符号数一说的.溢出表面上表现为两个正数相加会变成负数, 两个负数相加会变成正数. 正数和负数相加是不会溢出的. 这主要因为, 存储在内存中的数, 最高
bit
位是表现符号的, 但计算机只会执行加法, 即从最低位加,0+0=0 1+0=0 0+1=1 1+1=0
, 最后的情况会进1. 又因为补码的编码方式, 在两正或两负相加时, 可能会改变最高符号位的值.如上所言, 计算机执行加法其实并不在乎数是无符号还是有符号, 它只知道
1+1=0
再进1. 有无符号是人类的概念. 但计算机在做每次加法(其实减法也是加法)时, 都会改变一些标志位, 其中某些标志是用来比较大小(cmp
的本质是试探性执行减法再检测标志, 也就是说还是加法), 如ZF
标志检测相等. 还有些标志, 如进位标志CF
可检查无符号数的回绕(回绕时必然进位), 溢出标志OF
可检查有符号数的溢出.再次强调一遍, 每次执行加法运算都会置标志位, 再依据你想要有无符号运算, 再检测
CF/OF
标志.2, 整数的溢出就是溢出, 并不存在上/下溢. 只有在浮点数运算中才存在上/下溢的概念. 在这里涉及浮点数的表示格式, 相对来说更为复杂. 每种格式都有其表示的范围, 这里还要分为正负, 自然也有正上溢, 正下溢, 负上溢, 负下溢. 主要是浮点能表示的值是有范围的, 虽然远大于同字节的整数, 类似整数也有范围.
还有种相关的概念, 是关于缓冲区的上/下溢. 上溢是当一个超长的数据进入到缓冲区时,超出部分被写入上级缓冲区,下溢是当一个超长的数据进入到缓冲区时,超出部分被写入下级缓冲区, 都有可能导致一个程序或者操作系统崩溃. 但如果是这种理解就跟你说的整数运算不相关了.
也有人理解, 上溢是大于整数的最大值, 下溢是小于整数的最小值. 但这种理解非常不专业, 太表面了. 因为所谓的大于和小于对有无符号数产生的影响不同. 换句话说, 它同时包含了回绕和溢出两种概念, 但在
cpu
的标志中, 它俩是分开的.对于浮点数的运算有专门的硬件, 跟整数运算的
ALU
不是一个部件. 不确定浮点运算上/下溢是否会置相应标志位以表示.cpu
, 运算, 置位.这就是它的工作机制, 非常简洁, 只作一件事. 对错并不是由
cpu
来判断的, 它不知道这是不是你想要的. 有很多高效的算法依赖这种简洁机制.你这里的报错应该是指, 为什么程序在编译时不会警告, 而操作系统在运行时不会警告? 而显然它们都知道运算是否溢出或回绕了(通过检测标志). 通常情况下, 静态类型语言如
C
, 执行运算时是知道数据有无符号的, 自然也知道是检测CF/OF
位.其实关键是, '我'怎么知道你是不是就想依赖这个特殊的机制. 例如, 要执行一个4次循环:
这里就利用了
c
在相加4次后回绕成0. 这里只是一个很无聊的示例, 但你不能否认我这样写就是错的, 反而一般人是看不懂这样的程序的.编译时不违反语法, 运行时不产生中断. 这就是一个好程序, 至于是不是你想要的程序, 计算机并管不着.
如果只是设置警告的话, 我搜索了下, 并没有此选项. 这里要充分考虑到,
C
语言的一项设计哲学就是简洁, 相信程序员做正确的事. 它的编译器只给出极其有限的警告和错误提示. 况且, 一个熟练的程序员, 可以自己根据需要在程序中设置if
来判断自己的运算是否回绕或溢出. 你瞧, 这是自己的事.至于操作系统给警告就更扯了. 默认情况下, 程序不引起中断, 操作系统才不在乎程序在干什么呢.
不过, 对于缓冲区溢出, 编译器会给出警告, 有可能需要设置一些编译选项. 而操作系统同样不在乎. 程序是程序的事, 操作系统怎么知道你是不是就想它溢出呢. 如, 几乎所有的漏洞利用都是利用缓冲区溢出. 是的微软也阻止不了这些事, 虽然它想阻止, 虽然它也做了很多安全机制, 虽然锅都可以甩到
C/Cpp
的语言设计和编译器设计上.当然这一切都是建立在别人写的编译器和操作系统上, 如果这种认定让你不舒服, 就是你自己的事了, 你可以卷起袖子自己干. 你的地盘你做主.
让我们说的更明白些.
并非编译器检测不到回绕或溢出, 而是这个机制很难认定为
bug
或是feature
. 且非常多见. 你可能自己不写, 但你用的很多库函数的内部可能就使用此机制来高效运算. 再加上C
语言的设计哲学, 因此不警告是可以理解的. 相信我,C
语言有很多问题, 这可能是最不算问题的问题.至于操作系统, 你可能并没有相关的知识背景. 但你可以大概理解为, 操作系统将
CPU
时间分片, 依次赋予不同进程. 操作系统唯一的职责是在分片时间到时调度进程. 考虑到两方面, 一是程序执行整数加法运算的次数远多于你源码里写加法的数目, 二是如果编译器没将相关警告代码插入程序, 操作系统要做到整数溢出检查就几乎要, 每执行一条指令(希望你能明白指令和源码的区别)都要进入整数相加溢出中断(并没有此中断). 这几乎, 没法运行.更何况, 在汇编码里, 是看不出有无符号数的.应为数字运算是使用频率非常高的,如果每次都去进行检查会严重影响性能(比如C语言不检查数组越界,也是同一个原因),实际上这是可以做到的,比如C#语言中checked和unchecked操作符用于整型算术运算时控制当前环境中的溢出检查.
对于整数的溢出,CPU 是能够检测出来的,它会将检测结果记录到某个寄存器的某个位上(OF 标志位),这就是它给出的警告。