本帖最后由 machao 于 2019-8-22 23:21 编辑
使用keil C51的朋友注意了,版本V9.59以前的所有版本都有一个“坑”!
在v9.59的 Release Notes 中有一个修正!主要是避免8位有符号数与无符号数比较出错的问题(在少数情况下会发生)
Corrected: in rare cases a signed compare with unsigned char treated the unsigned char as signed. This happens if the unsigned char value results from a calculated assignment. Example:
char i;
unsigned char uc0 = 254;
char c1 = 0;
void main() {
if (c1 <= (uc0 += c1)) // uc0 is wrongly treated signed => -2 therefore the condition (0 <= -2) is false
i = 1;
else
i = 0;
}
in rare cases 翻译成中文意思:在极少数情况下........
=======================================================
在c语言中是这样规定的:常量无unsigned型。但一个非负值的整数可以赋值给unsigned整型变量,只要它的范围不超过变量的取值范围即可。
换句话说,一个常量,比如10,它是一个signed型!
在编写程序时,我们可以有意识的避免两个不同类型(unsigned与signed)变量之间的比较,但会忽视一个变量与常量的比较,比如,经常使用如下的语句:
uchar i;
if (i >= 10 ) {}
for(i=0;i<10;i++){}
尽管定义了变量 i 为unsigned,但常量10却为signed。所以上面两句语句都是不同类型的比较。按keil c51 V9.59的 Release Notes中说法,在“极少情况下”会出现错误的。
我查了一下,keil c51 V9.59是2018年4月推出的,说明在以前的版本中,“极少情况下”会出现错误的结果。
keil c51 V9.59以及以后的版本(V9.60、V9.60a)做了修正,防止了这种“极少情况下”会出现错误的现象发生,但其编译后的代码要比以前版本的长。
目前解决的办法有:
1/ 不考虑代码效率时,用V9.59以后的版本重新编译。
2/ 要考虑代码效率时,按keil c51 V9.59的 Release Notes中说法,使用强制转换,包括对常量。上面的例子改成:
uchar i;
if (i >= (uchar)10 ) {}
for(i=0;i<(uchar)10;i++){}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
Corrected: in rare cases a signed compare with unsigned char treated the unsigned char as signed. This happens if the unsigned char value results from a calculated assignment. Example:
char i;
unsigned char uc0 = 254;
char c1 = 0;
void main() {
if (c1 <= (uc0 += c1)) // uc0 is wrongly treated signed => -2 therefore the condition (0 <= -2) is false
i = 1;
else
i = 0;
}
Wrong code was generated:
0000 E500 R MOV A,uc0
0002 2500 R ADD A,c1
0004 F500 R MOV uc0,A
0006 D3 SETB C
0007 6480 XRL A,#080H
0009 F8 MOV R0,A |
000A E500 R MOV A,c1 |
000C 6480 XRL A,#080H |
000E 98 SUBB A,R0 |
000F 5004 JNC ?C0001 -> signed char compare
0011 750001 R MOV i,#01H
0014 22 RET
0015 ?C0001:
0015 E4 CLR A
0016 F500 R MOV i,A
0018 ?C0003:
0018 22 RET
The correct code is much longer because according to the C standard a cast to int is necessary:
0000 E500 R MOV A,uc0
0002 2500 R ADD A,c1
0004 FF MOV R7,A
0005 F500 R MOV uc0,A
0007 AD00 R MOV R5,c1
0009 ED MOV A,R5 |
000A 33 RLC A |
000B 95E0 SUBB A,ACC |
000D FC MOV R4,A -> cast of signed char to int
000E D3 SETB C
000F ED MOV A,R5 |
0010 9F SUBB A,R7 |
0011 7480 MOV A,#080H +> implicit cast of unsigned char to int
0013 F8 MOV R0,A |
0014 6C XRL A,R4 |
0015 98 SUBB A,R0 |
0016 5004 JNC ?C0001 -> signed int compare
0018 750001 R MOV i,#01H
001B 22 RET
001C ?C0001:
001C E4 CLR A
001D F500 R MOV i,A
001F ?C0003:
001F 22 RET
To generate smaller code the signedness of the values to be compared should be the same:
...
if ((unsigned char)c1 <= (uc0 += c1))
...
0000 E500 R MOV A,uc0
0002 2500 R ADD A,c1
0004 FF MOV R7,A
0005 F500 R MOV uc0,A
0007 E500 R MOV A,c1
0009 D3 SETB C
000A 9F SUBB A,R7 |
000B 5004 JNC ?C0001 -> unsigned char compare
000D 750001 R MOV i,#01H
0010 22 RET
0011 ?C0001:
0011 E4 CLR A
0012 F500 R MOV i,A
0014 ?C0003:
0014 22 RET
以前没有注意到这一点,好在这不是每次比较都会出现问题。但总是不放心。
如果程序中是两个变量(一个有符号,一个无符号)做比较时,还能够注意到。但一个无符号变量与常数比较时,就会忽视。
按说明中,要加强制转换,才能生成比较简洁的汇编代码,否则代码会增加的。
我在另外一个帖子中有例子。现在要这样写代码,常数也要强制转换,才能生成简洁代码。
// time0 中断服务,中断时间为50us, 运行模式12T
uchar time_1k_counter,time_100hz_counter,time_10hz_counter;
bit time_key_ok;
void Int_time0(void) interrupt T0_VECTOR
{
if(++time_1k_counter >= (uchar)10)
{
clk_1k = !clk_1k;
time_1k_counter = 0;
if (++time_100hz_counter >= (uchar)10)
{
clk_100hz = !clk_100hz;
time_100hz_counter = 0;
time_key_ok = true;
if (++time_10hz_counter >= (uchar)10)
{
clk_10hz = !clk_10hz;
time_10hzcounter = 0;
}
}
}
}
一周热门 更多>