汇编中有符号与无符号数以及CF,OF标志位的区分
汇编中有符号与⽆符号数以及CF,OF标志位的区分
漏水检测一次多少钱汇编中有符号与⽆符号数以及CF,OF标志位的区分
⼀、只有⼀个标准!手工制作玩具
  ⾸先需要知道,计算机对数值的存储采⽤补码形式存储,⼀来避免了+0和-0的尴尬,⼆来数值的加法和减法可以统⼀为补码的加法。   在汇编语⾔层⾯,定义变量的时候,没有 signed和unsignde 之分,汇编器统统将你输⼊的整数字⾯量当作有符号数(最⾼位的符号位根据输⼊的数值符号决定)处理成⼆进制补码存⼊到计算机中,只有这⼀个标准!
  汇编器不会区分有符号还是⽆符号然后⽤两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,⽽ db 236 汇编后也为 EC 。这⾥有⼀个⼩问题,思考深⼊的朋友会发现,db 是分配⼀个字节,那么⼀个字节能表⽰的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这⼀范围,怎么可以?是的,+236 的补码的确超出了⼀个字节的表⽰范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,⼀个字节装不下,但是,别忘
了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个“美观看顺序
丽的错误”,为什么这么说?因为,当你把 236 当作⽆符号数时,它汇编后的结果正好也是 EC ,这下皆⼤欢喜了,虽然汇编器只⽤⼀个标准来处理,但是借⽤了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你⼀个字节,你想输⼊有符号的数,⽐如 -20 那么汇编后的结果是正确的;如果你输⼊ 236 那么你肯定当作⽆符号数来处理了(因为236不在⼀个字节能表⽰的有符号数的范围内啊),得到的结果也是正确的。于是给⼤家⼀个错觉:汇编器有两套标准,会区分有符号和⽆符号,然后分别汇编。其实,你们被骗了。:-)
⼆、存在两套指令!
  第⼀点说明汇编器只⽤⼀个⽅法把整数字⾯量汇编成真正的机器数。但并不是说计算机不区分有符号数和⽆符号数,相反,计算机对有符号和⽆符号数区分的⼗分清晰,因为计算机进⾏某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和⽆符号数准备的。但是,这⾥要强调⼀点,⼀个数到底是有符号数还是⽆符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就⽤那⼀套处理有符号数的指令,当你认为你要处理的数是⽆符号的,那就⽤处理⽆符号数的那⼀套指令,计算机只根据指令对每⼀位进⾏计算,并不在乎这个⼆进制数据是有符号还是⽆符号,这就需要依靠程序员判断处理的数值是有符号还是⽆符号,仅此⽽已。
  加减法只有⼀套指令,因为这⼀套指令同时适⽤于有符号和⽆符号(刚才所说的补码优点)。⽽有
些指令,如:mul div movzx … 是处理⽆符号数的,⽽这些:imul idiv movsx … 是处理有符号的(也就是针对有符号和⽆符号提供两套指令)
举例来说:
  我们在汇编中,向内存中放⼊两个数据,⼀个字节x 为:0x EC ,⼀个字节 y 为:0x 02 。
  发现没有负号,都是正数,OK那么最⾼位的负号位设置为0,再对x进⾏截断处理,得到
x:
1 1 1 0 1 1 0 0
y:
0 0 0 0 0 0 1 0
  注意这⾥的x,如果x是个有符号数字,虽然我们给的是⼀个ec正数,但是由于截断处理,最⾼位为1,成了⼀个负数,⼜由于这是补码保存,原码就成了-20,y仍然为2
当作⽆符号数看时,第⼀位的符号位不看,x = 236 ,y = 2 。
  但是从计算机的⾓度看,这就是⼀串⼆进制,哪有什么有符号⽆符号数字
  下⾯⽤ add 指令进⾏加法运算,计算机开始⼯作,它只需要把每⼀位相加,仅此⽽已,它才不分什么有符号,⽆符号。
结果:
1 1 1 0 1 1 1 0
海竿钓鱼技巧  这个结果当作有符号数就是:-18 ,⽆符号数就是 238 。同样,计算机认为这仍然是⼀串⼆进制,所以add ⼀个指令可以适⽤有符号和⽆符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
  乘法运算就不⾏了,必须⽤两套指令,有符号的情况下⽤imul 得到的结果是:0x FF D8 就是 -40 。⽆符号的情况下⽤ mul ,得到:0x 01 D8 就是 472 。
三、 OF、CF、SF标志
  为什么说着三个标志位,因为我对汇编中⽆符号,有符号的疑惑就是从这三个标志位的学习⽽产⽣的。
  先看CF标志位,书上说CF标志位只对⽆符号数有意义,⾸先明⽩⼀点,即使是两个有符号数相加,也会导致CF的变动,并不是说有符号数,编译器不设置CF位。
因为CF的标志位的变动是由于最⾼有效位(如果对于8位数,就是第8位)向更⾼位(第9位)产⽣了进位或者借位⽽产⽣,⽽对于有符号数来说,最⾼位是符号位,它的变动和数值位的变动意义不⼀样。所以对于有符号数,CF也可能发⽣变动,但是它的变动是没意义的。⽽如果是⽆符号数,它的变动就意味中8位的内存或寄存器不⾜以保存数据,因为数据产⽣了进位或借位。
  再看OF标志位,它只对有符号数有意义,因为两个标准的8位有符号数据(标准指的是赋值的时候不要赋超过有符号数范围的数字,由于截断,即是8位能保存,保存进来的数据数值⼤⼩早就产⽣了变化),这2个数据只有同号(都为正或为负)相加才会溢出,也就是结果超过有符号数的范围。例如2个正数,符号位(第8位)都为0,相加后发⽣溢出,符号位由于第7位的进位变成了1,两个正数相加变为了负数?由此对OF产⽣了作⽤,如此来说OF的作⽤是由于符号位发⽣变化,如果是两个⽆符号数,最⾼位代表的并不是符号意义,产⽣了变动也是⽆意义的,所以说OF只对有符号数有意义。
居高声自远 非是藉秋风的意思
最后SF标志,有了上⾯的介绍,就能理解SF看的是最⾼位的符号位意义,对于⽆符号数来说,最⾼位代表的是数值意义,并不是符号意义。
四、可爱⼜可怕的c语⾔。
  为什么⼜扯到 c 了?因为⼤多数遇到有符号还是⽆符号问题的朋友,都是c⾥⾯的 signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在⽤的c编译器,⽆论gcc 也好,vc6 的cl 也好,都是将c语⾔代码编译成汇编语⾔代码,然后再⽤汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明⽩了c,⽽且,⽤机器的思维去考虑问题,必须⽤汇编。(我⼀般遇到什么奇怪的c语⾔的问题都是把它编译成汇编来看。)
  C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提⾼了思维层⾯(⽐汇编的机器层⾯⼈性化多了),⼜不⾄于离机器太远 (像c# ,Java之类就太远了)。当初K&R 版的c就是⾼级⼀点的汇编……:-)
  C⼜是可怕的,因为它把机器层⾯的所有的东西都反应了出来,像这个有没有符号的问题就是⼀例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举⼀例:
#include <stdio.h>
教师节的作文500字#include <string.h>
int main()
{
int x = 2;
char * str = "abcd";
int y = (x - strlen(str) ) / 2;
//注:原作者这样写,编译器可能会对其优化,直接使⽤右移移位指令⽽不是采⽤除法指令,改成3即可看到
printf("%d\n",y);
}
  结果应该是 -1 但是却得到:2147483647 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时,int类型被⾃动转换为unsigned int了,结果⾃然出乎意料。。。
观察编译后的代码,除法指令为 div ,意味⽆符号除法。解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2; 强制向有符号⽅向转换(编译器默认正好相反),这样⼀来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,⽤有符号处理指令 imul ,idiv 等得到的结果,与⽤ ⽆符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号⽆符号计算的问题,特别是存在讨厌的⾃动转换时,要倍加⼩⼼!(这⾥⾃动转换时,⽆论gcc还是cl都不提⽰)
为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。(完)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。