C51程序unsigned和signed类型数据判断时候需要注意的问题
这次帮同学在C51课上无脑写了个很简单的C 但是却遇到很奇怪的问题 所以就有了这篇文章
以下C51中断程序根据按下按键的不同用LED显示按键所对应数值(1~8)的平方
#include <reg51.h>
void main()
{
EA=1;
IT0=1;
EX0=1;
while(1)
{
}
}
void int0() interrupt 0
{
signed char a,x;
char LED[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
a=~P1;
if(a==0x01) x=1;
if(a==0x02) x=4;
if(a==0x04) x=9;
if(a==0x08) x=16;
if(a==0x10) x=25;
if(a==0x20) x=36;
if(a==0x40) x=49;
if(a==0x80) x=64;
P0= LED[x/10];
P2= LED[x%10];
}
当P1=0x7F运算后a=0x80时if(a==0x80) x=64;这句判断的结果是否 应该是真才对
当把signed char改成unsigned后判断正确
signed char时的汇编代码
25: a=~P1;
C:0x0948 E590 MOV A,P1(0x90)
C:0x094A F4 CPL A ;取反
C:0x094B FF MOV R7,A ;判断值在R7
26: if(a==0x01) x=1;
C:0x094C BF0103 CJNE R7,#0x01,C:0952 ;否的话跳下个判断 否则就赋值
C:0x094F 750801 MOV 0x08,#0x01
27: if(a==0x02) x=4;
C:0x0952 BF0203 CJNE R7,#0x02,C:0958
C:0x0955 750804 MOV 0x08,#0x04
28: if(a==0x04) x=9;
C:0x0958 BF0403 CJNE R7,#0x04,C:095E
C:0x095B 750809 MOV 0x08,#0x09
29: if(a==0x08) x=16;
C:0x095E BF0803 CJNE R7,#0x08,C:0964
C:0x0961 750810 MOV 0x08,#0x10
30: if(a==0x10) x=25;
C:0x0964 BF1003 CJNE R7,#0x10,C:096A
C:0x0967 750819 MOV 0x08,#0x19
31: if(a==0x20) x=36;
C:0x096A BF2003 CJNE R7,#0x20,C:0970
C:0x096D 750824 MOV 0x08,#0x24
32: if(a==0x40) x=49;
C:0x0970 BF4003 CJNE R7,#0x40,C:0976
C:0x0973 750831 MOV 0x08,#0x31
33: if(a==0x80) x=64;
34:
C:0x0976 EF MOV A,R7 ;头疼的问题发生在这里
C:0x0977 33 RLC A
C:0x0978 95E0 SUBB A,ACC(0xE0)
C:0x097A FE MOV R6,A
C:0x097B EF MOV A,R7
C:0x097C 6480 XRL A,#P0(0x80)
C:0x097E 4E ORL A,R6
C:0x097F 7003 JNZ C:0984
C:0x0981 750840 MOV 0x08,#0x40
35: P0= LED[x/10];
C:0x0984 E508 MOV A,0x08 ;x在D:0x08
C:0x0986 75F00A MOV B(0xF0),#0x0A
C:0x0989 1208F6 LCALL C?SCDIV(C:08F6)
C:0x098C 2409 ADD A,#0x09 ;LED在D:0x09
C:0x098E F8 MOV R0,A
C:0x098F E6 MOV A,@R0
C:0x0990 F580 MOV P0(0x80),A
改成unsigned之后的汇编
25: a=~P1;
C:0x0926 E590 MOV A,P1(0x90)
C:0x0928 F4 CPL A
C:0x0929 FF MOV R7,A
26: if(a==0x01) x=1;
C:0x092A BF0103 CJNE R7,#0x01,C:0930
C:0x092D 750801 MOV 0x08,#0x01
27: if(a==0x02) x=4;
C:0x0930 BF0203 CJNE R7,#0x02,C:0936
C:0x0933 750804 MOV 0x08,#0x04
28: if(a==0x04) x=9;
C:0x0936 BF0403 CJNE R7,#0x04,C:093C
C:0x0939 750809 MOV 0x08,#0x09
29: if(a==0x08) x=16;
C:0x093C BF0803 CJNE R7,#0x08,C:0942
C:0x093F 750810 MOV 0x08,#0x10
30: if(a==0x10) x=25;
C:0x0942 BF1003 CJNE R7,#0x10,C:0948
C:0x0945 750819 MOV 0x08,#0x19
31: if(a==0x20) x=36;
C:0x0948 BF2003 CJNE R7,#0x20,C:094E
C:0x094B 750824 MOV 0x08,#0x24
32: if(a==0x40) x=49;
C:0x094E BF4003 CJNE R7,#0x40,C:0954
C:0x0951 750831 MOV 0x08,#0x31
33: if(a==0x80) x=64;
34:
C:0x0954 BF8003 CJNE R7,#P0(0x80),C:095A ;更改之后这里的判断相同了
C:0x0957 750840 MOV 0x08,#0x40
35: P0= LED[x/10];
C:0x095A E508 MOV A,0x08
C:0x095C 75F00A MOV B(0xF0),#0x0A
C:0x095F 84 DIV AB
C:0x0960 2409 ADD A,#0x09
C:0x0962 F8 MOV R0,A
C:0x0963 E6 MOV A,@R0
C:0x0964 F580 MOV P0(0x80),A
对于signed时候的汇编部分进行分析:
33: if(a==0x80) x=64;
34:
C:0x0976 EF MOV A,R7 ;头疼的问题发生在这里
C:0x0977 33 RLC A ;A、CY循环左移一位,影响标志位P、CY
C:0x0978 95E0 SUBB A,ACC(0xE0) ;(A) - (Rn) [(direct), ((Ri)), data] - CY → A
C:0x097A FE MOV R6,A
C:0x097B EF MOV A,R7
C:0x097C 6480 XRL A,#P0(0x80) ;(A) ⊕ Rn [(direct), ((Ri)), data] → A,影响标志位P
C:0x097E 4E ORL A,R6 ;(A) ∨ Rn [(direct), ((Ri)), data] → A,影响标志位P
C:0x097F 7003 JNZ C:0984
C:0x0981 750840 MOV 0x08,#0x40
C:0x0977 33 RLC A
;循环移位之前如果最高位是1 那么CY=1 否则CY=0
C:0x0978 95E0 SUBB A,ACC(0xE0)
;这里其实A=A-A-CY 那么CY=1的时候这里就是FF CY=0的时候这里就是00
C:0x097C 6480 XRL A,#P0(0x80)
;这里是把程序变量a(也就是P1取反之后的数值) 和0x80异或 那么相等的时候ACC中应该是全0 否则不同位置1
C:0x097E 4E ORL A,R6
;R6中是前面的那个CY 这里进行或运算 换言之如果CY=0 ACC=异或0x80的那个数 如果CY=1时 这里或好之后就算原先异或0x80之后是全0 这里也等于置位了 结果不为0
C:0x097F 7003 JNZ C:0984
;结果非0则跳转 否则的话x=64
分析:
C:0x0977 33 RLC A
C:0x0978 95E0 SUBB A,ACC(0xE0)
C:0x097A FE MOV R6,A
如果原先传进来的数最高位是1 那么R6中的是FF 否则就是00
C:0x097B EF MOV A,R7
C:0x097C 6480 XRL A,#P0(0x80)
C:0x097E 4E ORL A,R6
C:0x097F 7003 JNZ C:0984
和0x80异或之后 结果全0应该就是一样了 否则结果不是全0 但是他这里又把前面计算的R6又或了一次 也就是说前面运算CY=1的时候这里结果肯定不为全0
总结:
只要传进去变量a的最高位是1 那么这句IF的判断肯定是否
再来看下我们的IF语句: if(a==0x80)
我们定义为signed的时候 他应该是把0x80当作一个无符号数来处理的
这样如果a的最高位是1 也就是负数的时候 那么一个负数肯定不会是无符号数
对于这样一个
if(a==b)的判断
这样判断成立时a的范围也就被局限在了0-127的范围之内 这也正是一个有符号数真值=无符号数真值的范围
这段signed的时候的汇编代码的功能是:
对于一个符号数是否等于一个无符号数的判断
这里的0x80被认作是一个无符号数
当a=0x80的时候 a其实是有符号数中的-128 而无符号数的0x80是指的128 数据上一样 但是实际上是代表的是不同的数值
修改这里的代码为if((a^0x80)==0) x=64;
生成以下汇编
C:0x0976 EF MOV A,R7
C:0x0977 6480 XRL A,#P0(0x80)
C:0x0979 7003 JNZ C:097E
C:0x097B 750840 MOV 0x08,#0x40
成功完成了原先我想要的纯粹的位判断
结论:
在编译的时候 编译器会对数据类型进行一定的判断 保证对于数值的判断是真值相同 而不是简单的二进制位相等
把代码进一步修改 查看生成的汇编代码
33: if((a^0x80)==0x01) x=64;
34:
C:0x0976 EF MOV A,R7
C:0x0977 6480 XRL A,#P0(0x80)
C:0x0979 FF MOV R7,A
C:0x097A BF0103 CJNE R7,#0x01,C:0980
C:0x097D 750840 MOV 0x08,#0x40
再更改一下
33: if((a^0x80)==0x80) x=64;
34:
C:0x0976 EF MOV A,R7
C:0x0977 6480 XRL A,#P0(0x80)
C:0x0979 FF MOV R7,A
C:0x097A BF8003 CJNE R7,#P0(0x80),C:0980
C:0x097D 750840 MOV 0x08,#0x40
再更改一下
33: if((a^0x80)==0xFF) x=64;
34:
C:0x0976 EF MOV A,R7
C:0x0977 6480 XRL A,#P0(0x80)
C:0x0979 FF MOV R7,A
C:0x097A BFFF03 CJNE R7,#0xFF,C:0980
C:0x097D 750840 MOV 0x08,#0x40
对这些明确类型的数值数据进行位运算之后的中间结果编译器就认为是纯粹的非数值数据来进行按位判断了