打印

Keil MDK 5.0,可能的bug

[复制链接]
4326|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
我也不相信mdk会出这样的bug,我是无意间发现的,希望不是软件的问题。不截图,描述下吧,无符号整型数组里的一个元素记为A吧,正常调整范围是0 1 2 3,所以采用求余的方式,增量时 A =(A + 1)  %  4;减量时A =(A - 1)  %  4;增量计算正常。减量时,如果A=0,执行一次,A = 0xffff(应该A = 3),再执行一次,A = 2;再执行一次 A = 1;再执行一次A = 0;重复前面的过程。
写成 if(A > 0) A --;
       else A = 3;
一定执行正常。
因为一般用增量方式调整,减量一般不用,所以没想到这个会出错,无意间调了一下,发现问题了。
请高手看看是什么原因造成的,如果是软件bug,很是失望。

相关帖子

沙发
ayb_ice| | 2014-7-11 11:08 | 只看该作者
本帖最后由 ayb_ice 于 2014-7-11 11:12 编辑

上代码

这个肯定是当成了有符号处理了

写成
U16 a = 0;
a = (a-1) % 4u;

应该可以了


使用特权

评论回复
板凳
逍遥派掌门| | 2014-7-11 12:14 | 只看该作者
减量时,如果A=0,执行一次,A = 0xffff(应该A = 3),再执行一次,A = 2;再执行一次 A = 1;再执行一次A = 0;
==========================================================================================================
作为无符号数 A ,
当 A = 0 ;  ( A - 1 )  % 4  怎么会变成楼主认为的 3 呢?

使用特权

评论回复
地板
zhaoyu2005|  楼主 | 2014-7-11 12:16 | 只看该作者
ayb_ice 发表于 2014-7-11 11:08
上代码

这个肯定是当成了有符号处理了

可以确定定义的是无符号数,全局数组。如果按有符号数处理,那在A = 0xffff时,再减量执行一次就A = 2,就说不通了。
减量部分代码
case        4:        Parameters[Numb_Of_Gas_Unit] = (Parameters[Numb_Of_Gas_Unit] - 1) % 4;
                break;

增量部分代码
case        4:        Parameters[Numb_Of_Gas_Unit] = (Parameters[Numb_Of_Gas_Unit] + 1) % 4;
                break;
增量部分结果正常,减量部分只在A =0时执行出错,其他又对了

使用特权

评论回复
5
zhaoyu2005|  楼主 | 2014-7-11 12:26 | 只看该作者
逍遥派掌门 发表于 2014-7-11 12:14
减量时,如果A=0,执行一次,A = 0xffff(应该A = 3),再执行一次,A = 2;再执行一次 A = 1;再执行一次A ...

A = 0时,减1,A = 0xffff,除4求余结果就是3,其实不管是无符号整型还是字符型,结果都是3。不知道你的计算步骤,以及计算结果,交流下。代码写多了,就是发现一些技巧,比如值只0 1变化,不论加减,就不判断了,直接异或。

使用特权

评论回复
6
cjseng| | 2014-7-11 13:21 | 只看该作者
看汇编

使用特权

评论回复
7
zhaoyu2005|  楼主 | 2014-7-11 13:28 | 只看该作者
cjseng 发表于 2014-7-11 13:21
看汇编

看来只能走这条路了,ARM的汇编,实在是不熟,要是51的,不用看书,直接就能分析了。只能对着指令表慢慢计算了

使用特权

评论回复
8
ayb_ice| | 2014-7-11 13:31 | 只看该作者
关键是后面那个4当成了有符号数了,

写成4u就可以了

使用特权

评论回复
9
zhaoyu2005|  楼主 | 2014-7-11 15:12 | 只看该作者
1   0x080095B4 8928      LDRH     r0,[r5,#0x08]   //将r5+0x08结果对应地址的数据高半字清零,然后加载到r0,r5+0x08的结果
                                             对应数组元素的地址
2   0x080095B6 1E40      SUBS     r0,r0,#1   //r0减1,并保存
3   0x080095B8 17C3      ASRS     r3,r0,#31   //将r0的值算术右移31位,然后传给r3
4   0x080095BA EB007393  ADD      r3,r0,r3,LSR #30    //r3逻辑右移30位,然后和r0相加,然后结果传给r3
5   0x080095BE F0230303  BIC      r3,r3,#0x03   //清除r3的低2位,并保存
6   0x080095C2 1AC0      SUBS     r0,r0,r3   //r0减r3,并保存
7   0x080095C4 8128      STRH     r0,[r5,#0x08]   //将r0的值传给r5+0x08结果对应的地址
以上对应减量的汇编代码,//后面是我自己添加的注释,方便不熟悉的朋友看,最左边的数字是我加的,方便下面的说明;
实际程序中,变量是Parameters[Numb_Of_Gas_Unit] ,为了方便,还以A代替,对应的地址为 r5+ 0x08的结果,
详细过程看下面:
当A = 0时,                         A=0xffff时,
执行指令1后,r0 =0;                r0=0x0000ffff;
执行指令2后,r0 =0xffffffff;          r0 =0x0000fffe;
执行指令3后,r3 =0xffffffff;          r3 =0;
执行指令4后,r3 =0x02;            r3 =0x0000fffe;
执行指令5后,r3 = 0;               r3 =0x0000fffc;
执行指令6后,r0 =0xffffffff;          r3 =0x02;
执行指令7后,A=0xffff;             A =0x02;
从以上分析,当r0 = 0,减一后r0 = 0xffffffff,然后算术右移31位结果还是0xffffffff,问题应该就出在这儿,估计真的是按有符号数计算了。在写这个之前,有坛友说按有符号数计算了,没明白,以为是把A当有符号数了,实际是把4当有符号数了,在ayb_ice的两次提醒下并进行测试,才明白。自认为基础还行,看来差得不少,把4写成4u后计算结果正确,汇编代码附上,其他人引以为戒。
0x080095B4 7A28      LDRB     r0,[r5,#0x08]
0x080095B6 1E40      SUBS     r0,r0,#1
0x080095B8 F0000003  AND      r0,r0,#0x03
0x080095BC 8128      STRH     r0,[r5,#0x08]

使用特权

评论回复
10
zhaoyu2005|  楼主 | 2014-7-11 15:21 | 只看该作者
一直以为默认的是无符号数,看来得小心了。这个用法以前也用过,是正常的,要不早就应该发现了

使用特权

评论回复
11
ayb_ice| | 2014-7-11 15:28 | 只看该作者
你这个求余的算法只适合2^n情况(减法时),不可移植,

不推荐使用

使用特权

评论回复
12
逍遥派掌门| | 2014-7-11 16:58 | 只看该作者
zhaoyu2005 发表于 2014-7-11 12:26
A = 0时,减1,A = 0xffff,除4求余结果就是3,其实不管是无符号整型还是字符型,结果都是3。不知道你的 ...

估计编译器会根据具体的函数将对应的参数自动转换为默认的数据类型。如果真的话,这就是个陷阱了。

个人喜欢这样处理:

const U16 B[4]={0,1,2,3};
U16 index=0,direction=0,A;

if (direction ==0 )      // 增
{
  if (index< 3) {index++;} else index = 0;
}
else                     // 减
{
   if (index> 0) {index--;} else index = 3;
}

A=B[index];

使用特权

评论回复
13
zhaoyu2005|  楼主 | 2014-7-11 21:18 | 只看该作者
ayb_ice 发表于 2014-7-11 15:28
你这个求余的算法只适合2^n情况(减法时),不可移植,

不推荐使用

就是针对2的N次方才用这种方式的,要是其他数,老实判断处理了

使用特权

评论回复
14
zhaoyu2005|  楼主 | 2014-7-11 21:23 | 只看该作者
逍遥派掌门 发表于 2014-7-11 16:58
估计编译器会根据具体的函数将对应的参数自动转换为默认的数据类型。如果真的话,这就是个陷阱了。

个人 ...

对于非2的n次方的数,当然是用此方法了。这次就是软件默认将4处理成有符号数了,现在才理解ucos里定义常量后边带u,以前不知道啥作用,也没深究,应该就是为了防止编译器默认成有符号数

使用特权

评论回复
15
zhaoyu2005|  楼主 | 2014-7-12 22:26 | 只看该作者
命名分数分配完了,显示可分配分为0,但是结贴提示分数分配不对,网站有问题吧

使用特权

评论回复
16
MK60| | 2014-7-12 23:10 | 只看该作者
本帖最后由 MK60 于 2014-7-12 23:11 编辑

楼主,我在MDK5.11下试了没有问题啊?



你再试试这个:
  unsigned short int USI = -1;
  long int LI = 0;
  
  if (USI < LI) {   
    while (1);    // Why?
  }

使用特权

评论回复
17
MK60| | 2014-7-13 13:41 | 只看该作者
本帖最后由 MK60 于 2014-7-13 13:45 编辑
zhaoyu2005 发表于 2014-7-11 15:12
1   0x080095B4 8928      LDRH     r0,[r5,#0x08]   //将r5+0x08结果对应地址的数据高半字清零,然后加载 ...

对于表达式 A = (A - 1) % 4 ,如果把A定义为
  unsigned short int = 0;
则第一次运行结果将为0xFFFF,这是为何?

按照C语言的语法我们来分析一下。

第一次运行时,对表达式中的(A - 1)求值,A总会被提升为int类型,而常数“1”“4”的类型也为int类型,
所以(A - 1)的结果为“-1”,-1 % 4 的结果为“-1”(即0xFFFFFFFF),最后这个0xFFFFFFFF将被截短为0xFFFF赋给A,故A等于0xFFFF。
在这个过程中,编译器先后进行了两次类型转换。

第二次运行时,A的值0xFFFF,提升为32位的int类型值为0x0000FFFF,即十进制的65535,
所以(A - 1)的结果为65534,65534 % 4 的结果为2,故最后A等于2。

第三次运行时,A的值为2,提升为32位的int类型值为0x00000002,即十进制的2,
所以(A - 1)的结果为1,1 % 4 的结果为1,故最后A等于1。

第四次运行时,A的值为1,提升为32位的int类型值为0x00000001,即十进制的1,
所以(A - 1)的结果为0,0 % 4 的结果为0,故最后A等于0。

之后重复以上过程。

--------------------------------------------------------------------------

为了得到预期结果,可以使用以下几种方式之一:

  A = (A - 1u) % 4;
  A = (A - 1) % 4u;
  A = ((unsigned int)A - 1) % 4;

对表达式A = (A - 1u) % 4,
第一次运行时,A(值为0)首先被提升为int类型,因常数“1u”为unsigned int类型,故A再次被提升为unsigned int类型,
所以(A - 1)的结果为无符号整数0xFFFFFFFF,即十进制的4294967295,4294967295 % 4 的结果为3,故最后A等于3。
注意,在这个表达式中,常数“4”也将被提升为unsigned int类型。在整个过程中,编译器先后进行了三次类型转换。

对表达式A = (A - 1) % 4u,
第一次运行时,A(值为0)被提升为int类型,(A - 1)的结果为int类型的“-1”,
因常数“4u”为unsigned int类型,故(A - 1)的结果“-1”将被提升为unsigned int类型,即当成无符号整数0xFFFFFFFF,即十进制的4294967295,
4294967295 % 4 的结果为3,故最后A等于3。
在整个过程中,编译器先后进行了三次类型转换。

对表达式A = ((unsigned int)A - 1) % 4,
第一次运行时,A(值为0)即被提升为unsigned int类型,随后的int型常数“1”“4”也都将被提升为unsigned int类型参与运算,故最后结果同上,A等于3。
在整个过程中,编译器先后进行了三次类型转换。

--------------------------------------------------------------------------

对表达式 A = (A - 1) % 4 来说,如果把A定义为以下三种类型之一,都能得到预期的结果。
  unsigned int = 0;
  unsigned long int = 0;
  unsigned long long int = 0;

语言分析的方法和结果同上。

--------------------------------------------------------------------------

楼主的解释有些勉强,没有从语法上找到根本的原因所在。
事实表明,在众多论坛上见到无数质疑编译器语法BUG的讨论,最后99.99999%以上都被证明是其本人对C语言基础的无知或误解
  ————要知道,那些搞编译器的家伙可是编程语言的天才和专家。



使用特权

评论回复
18
zhaoyu2005|  楼主 | 2014-7-14 08:17 | 只看该作者
没办法,水平有限

使用特权

评论回复
19
code| | 2014-7-14 11:51 | 只看该作者
楼主真笨,既然是 A =(A + 1)  %  4,
为什么不这样呢?? A =(A + 1) &3
进行与运算(保留低2位),比起求余,简单

使用特权

评论回复
20
zhaoyu2005|  楼主 | 2014-7-14 12:26 | 只看该作者
code 发表于 2014-7-14 11:51
楼主真笨,既然是 A =(A + 1)  %  4,
为什么不这样呢?? A =(A + 1) &3
进行与运算(保留低2位),比起求余,简 ...

没想起来,这招儿好

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

2940

帖子

9

粉丝