C 51プログラムunsignedとsignedタイプデータ判断時に注意すべき問題

24572 ワード

今度の学友を手伝ってC 51の授业の上で头がなくてとても简単なCを书いてしかしとても奇怪な问题に出会ってだからこの文章がありました
以下のC 51割り込みプログラムは、押下ボタンに応じて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];



}

P 1=0 x 7 F演算後a=0 x 80のときif(a=0 x 80)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だったらR 6の中の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

 
0 x 80以降と同じ結果になるはずです.そうしないと結果は0ではありませんが、彼はここで前に計算したR 6をもう一度、つまり前にCY=1を演算したとき、ここの結果は0ではないに違いありません.
 
まとめ:
変数aの最高位が1である限り、このIFの判断は肯定的かどうか
私たちのIF文を見てみましょう:if(a=0 x 80)
私たちがsignedと定義したとき、彼は0 x 80を符号なしの数として処理したはずだ.
このようにaの最高位が1である負数である場合、1つの負数は無符号数ではないに違いない.
 
このような
if(a=b)の判断
このように判断が成立するとaの範囲も0-127の範囲に限定され、これも符号数の真値=符号数の真値がない範囲である
 
このsignedのときのアセンブリコードの機能は:
1つのシンボル数が1つの無シンボル数に等しいか否かの判断
 
ここで0 x 80は符号なし数として認識される
a=0 x 80の場合、aは符号数のうち-128があり、符号数のない0 x 80は128データと同じであるが、実際には異なる数値を表す
 
ここでのコードをif((a^0 x 80)=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

 
これらの明確なタイプの数値データをビット演算した後の中間結果コンパイラは,純粋な非数値データとしてビット判定を行う.