打印

难道我发现了Keil的BUG?请各位高手看一看——有图有真相

[复制链接]
楼主: XIANSir
手机看帖
扫描二维码
随时随地手机跟帖
41
ayb_ice| | 2011-3-1 09:33 | 只看该作者 回帖奖励 |倒序浏览
我也期待高人出现,不对,应该是超人出现

使用特权

评论回复
42
linqing171| | 2011-3-1 09:38 | 只看该作者
41# ayb_ice


http://topic.csdn.net/u/20080827 ... 6-498e957cd6b2.html

复制过来内容如下:
多谢很多兄弟在上面跟贴。

其实我问的是为什么要先提升再取反,而不是怎么算结果会是多少。

多谢各位指点。

Operands that are smaller than an int (such as bool and char ) are converted to int before the operator is applied.
这句话或许是正解。
不过感觉这东东还是不自然,在同一类型unsigned char的两个数 之间操作理论上是不要提升。或许这就是传说中的规矩和潜规则?
对我有用[0]丢个板砖[0]引用举报管理TOP

weilianwl
(weilianwl)
等 级:
#26楼 得分:0回复于:2009-09-28 10:43:37
我也被这个问题困扰了好久,终于找到。因为在C89和C99标准中,按位操作的操作符(~、&、等)需要整型作为操作数,故按这个标准,字符型先被扩展为Int类型后再做运算

使用特权

评论回复
43
linqing171| | 2011-3-1 09:45 | 只看该作者
C语言中的整型提升(integral promotion)
刚百度到的。
以前根据经验粗略的知道一些 。
但是这个 位变量 取反的 事情 ,还是第一次 见到。
中文的关于VC6 取反+移位的讨论很多 。

使用特权

评论回复
44
XIANSir|  楼主 | 2011-3-1 10:05 | 只看该作者
42# linqing171
我也被这个问题困扰了好久,终于找到。因为在C89和C99标准中,按位操作的操作符(~、&、等)需要整型作为操作数”“


此说法对于VC++或许是对的,但对KEIL肯定不对。


因为我已经试过了:
bit b1 ,b2;
b1 = 1;
b2 =~b2;


KEI为b2 =~b2;产生的汇编代码有三条:
1、将b1拷贝到位寄存器”C"中
2、CPL C 即对位寄存器按位取反并将结果存于C
3、将C中的值赋给b2


而对于CPL按位取反指令,手册上有这样的解释:Description: CPL complements operand, leaving the result in operand. If operand is a single bit then the state of the bit will be reversed.


可见,KEIL中对于位变量(bit定义的变量)使用CPL按位取反指令不需要类型提升。而CPL指令对于位变量的操作就是反转——这正是我们想要的

使用特权

评论回复
45
ayb_ice| | 2011-3-1 10:28 | 只看该作者
LS
在KEIL中bit和位域中的位肯定不是同一个概念,否则没有必要扩展bit关键字了
总之出现这种情况我肯定认为是自己不对,自己没有完全理解C。。。
C虽简单,但要真正理解,精通还是大有**的

使用特权

评论回复
46
johnwjl| | 2011-3-1 10:47 | 只看该作者
我就纳闷,为什么一定要用“~”呢?
位取反用“!”才是王道!
告诫LZ一声:永远不要编写依赖编译器的C代码!
如果理解不了,5年后再回来看此贴吧。

使用特权

评论回复
47
123jj| | 2011-3-1 10:53 | 只看该作者
实在看不过去了,这是一个不是问题的问题,找这个问题也很无聊~~~

在C语言中,~运算符是将数据按位求反,先将bit类数据扩展成标准char pin数据类型,再求反。

如要想按照LZ的思路,让程序正常运行,只需加一个强制数据类型转换(bit)即可。
        pin.t_3 = (pin.t_1 && pin.t_2);
        pin.b_3 = (~(bit)pin.b_1 && ~(bit)pin.b_2);
        pin.t_4 = ((~(bit)pin.b_1) && (~(bit)pin.b_2));
        pin.b_4 = ((pin.b_1==0) && (pin.b_2==0));

请注意:
强制数据类型转换(bit)不能加在~按位求反运算后,否则,按照标准char pin数据类型求反的结果,由于求反位存储在原定义的BIT位置,强制数据(bit)型转换后,只要不为0, 就转换成1, 引起结果不准确!
示范程序如下面所示:
        pin.t_3 = (pin.t_1 && pin.t_2);
        pin.b_3 = ((bit)~pin.b_1 && (bit)~pin.b_2);   错误的强制数据类型转换
        pin.t_4 = (((bit)~pin.b_1) && ((bit)~pin.b_2));  错误的强制数据类型转换
        pin.b_4 = ((pin.b_1==0) && (pin.b_2==0));

使用特权

评论回复
48
XIANSir|  楼主 | 2011-3-1 11:16 | 只看该作者
本帖最后由 XIANSir 于 2011-3-1 11:34 编辑

我想,我明白了其中的道理了,请大家想想是不是这么个原理:

首先,大家一定要把位域和bit变量划清界限!
二者虽然都能达到定义位变量的目的,但是位域是C语言标准中的内容,为了达到兼容所有的CPU,C语言标准不能假设处理位域变量的CPU都有位处理单元,
所以,对于位域的按位取反操作,必须进行类型提升(在16位机和32位机上提升为int,在8位机上提升为char)。尽管8051确实有位存储单元和位处理机构,
但为了达到和标准C的兼容,也不能对位域使用“位处理单元C”,而只能按照没有位处理单元的CPU那样进行类型提升再做按位取反。

但是对于bit定义的位变量则不同,因为bit类型是KEIL专为8051对C语言的扩展,所以没有考虑兼容性一说,KEIL对于bit变量可以任意的使用位处理单元。
这也就解释了那八幅程序代码中为什么前四幅的结果和后四幅的不同:因为他们确实是非常不同的——尽管形式上看是一样的。

好了,由于后四幅的结果很符合常理,所以我们就不再讨论后四幅的程序现象了。
接下来我们着重分解一下:前四幅程序是怎样产生“诡异的结果的”

看程序(为了不分散注意力,我对代码进行了简化):
void main()
{        
        struct {
                uchr b_1        : 1;
                uchr b_2        : 1;
                uchr b_3        : 1;
                uchr b_4        : 1;
                uchr t_1        : 1;
                uchr t_2        : 1;
                uchr t_3         : 1;
                uchr t_4        : 1;
        }pin = {0,1,0,0};

        pin.b_1 = 1;
        pin.b_2 = 0;
        pin.t_1 = ~pin.b_1;
        pin.t_2 = ~pin.b_2;

        pin.t_3 = (pin.t_1 && pin.t_2);
        ...
        pin.t_4 = ((~pin.b_1) && (~pin.b_2));
        ..

        while(1);
}

pin.t_3 = (pin.t_1 && pin.t_2);
pin.t_4 = ((~pin.b_1) && (~pin.b_2));

这两句代码,一样吗???
看似一样,但一旦考虑了类型提升之后,就会发现他们非常不一样,这很具有误导性——我之前也认为一样,所以产生了要给KEIL提BUG的笑话。

为什么不一样呢,
因为(pin.t_1 && pin.t_2)是对两个位变量进行进行逻辑与
而pin.t_4 = ((~pin.b_1) && (~pin.b_2));其实是在对两个整数进行逻辑与——记得类型提升吗??

实际上,
pin.t_1 = ~pin.b_1;
pin.t_2 = ~pin.b_2;
两个操作中也进行了类型提升——因为标准C中的按位取反必须对整型进行应用(8位机中是char),然后位变量的按位取反操作的结果被从那个临时整型值中“(聪明地)正确的取了出来”

请注意,我这里强调聪明地正确的取了出来,而不是进行直接的降类型转换,因为类型转换是“蛮力型,粗暴的”——所以,升类型转换总能的到正确的结果,而降类型转换却很可能得到错误的结果:

不信的话,你可以比较一下下面的两组式子:
pin.t_1 = ~pin.b_1;
pin.t_2 = ~pin.b_2;
       VS
pin.t_1 = (bit)(~pin.b_1);
pin.t_2 = (bit)(~pin.b_2);
后面这一组等式并不能得到正确的结果,因为整型向bit型转换的方式只是简单的:取最低位。对于pin.t_1 = (bit)(~pin.b_1);取最低位肯定是对的,因为pin.b_1本来就是程序的最低位,但是对于
pin.t_2 = (bit)(~pin.b_2);肯定就是错误的了,因为pin.b_2是第二位。

说这些的原因是告诉大家:
pin.t_4 = ((bit)(~pin.b_1) && (bit)(~pin.b_2));并不能得到正确的结果。

但是我还是有一个疑问,为什么这样也得不到正确的结果呢:
pin.t_4 = ((pin.t_1=~pin.b_1) && (pin.t_2=~pin.b_2));

看来,位域的“按位操作”水很深啊,没有学透彻,最好少用。

使用特权

评论回复
49
XIANSir|  楼主 | 2011-3-1 11:24 | 只看该作者
47# 123jj 高人终于出现了。
高人啊,您的确是对的,尽管在看到您的帖子之前我就已经想明白了,但还是非常感谢。

另外,这对于您也许不是一个问题,但对于我们不懂的人来说,这绝对是一个巨大的问题。

再次感谢您的回复。

使用特权

评论回复
50
ayb_ice| | 2011-3-1 11:36 | 只看该作者
本帖最后由 ayb_ice 于 2011-3-1 11:53 编辑

其实这和提升没有关系,本质应该是~,&,|不对,而应该是用!,&&,||....
我测试过禁止KEIL的整型提升功能,结果一样的
使用不正确的这些~,&,|操作位域结果分析如下
首先使用位域一定会提升,而使用~的结果是对字节操作接着使用&,结果仍是字节,最后再把字节转换成位域,这样肯定达不到LZ想要的结果,但按规则却是正确的
至于具体的过程我没有兴趣分析,因为这本身就不符合规则,要改的是程序

使用特权

评论回复
评分
参与人数 1威望 +1 收起 理由
123jj + 1
51
123jj| | 2011-3-1 12:13 | 只看该作者
47# 123jj 高人终于出现了。
高人啊,您的确是对的,尽管在看到您的帖子之前我就已经想明白了,但还是非常感谢。

另外,这对于您也许不是一个问题,但对于我们不懂的人来说,这绝对是一个巨大的问题。

再次感谢您 ...
XIANSir 发表于 2011-3-1 11:24



首先更正一下,俺不是什么高人,俺不懂C语言,不搞技术也不做电工,C语言中,常用的几十个基本关键词俺都写不全,只能说大概了解一下C语言,跟在二姨家的高人后面捡点菜,学点**毛蒜皮小本事,真正的高手是象ayb_ice这类的高手奇人,一针见血,说到点子上去了~~~

二姨家的奇人高手真的很多很多,数不胜数,祝LZ早成大器~~~

使用特权

评论回复
52
123jj| | 2011-3-1 12:19 | 只看该作者
对C语言的编程,建议LZ尽量使用50楼ayb_ice老师推荐的方法,正确使用逻辑关键词 !,&&,||....
而不要不正确的使用这些位域操作 ~,&,|....
有助于LZ提升程序的质量,减少出错概率。。。。

使用特权

评论回复
53
XIANSir|  楼主 | 2011-3-1 12:25 | 只看该作者
本帖最后由 XIANSir 于 2011-3-1 12:27 编辑

50# ayb_ice
50# ayb_ice

“首先使用位域一定会提升,而使用~的结果是对字节操作接着使用&,结果仍是字节,最后再把字节转换成位域,这样肯定达不到LZ想要的结果”

前辈错了:
首先你上面描述的等式是:
pin.t_4 = ((~pin.b_1) & (~pin.b_2));
而这个等式得到的结果肯定是正确的,我已经试过了,而且前面也有人已经有人提出这种操作方法的正确性。

这也就说明,将位域提升为字节,再把字节降为位域,肯定能得到正确的结果,也就是:
pin.t_2 = ~pin.b_2;肯定能得到正确结果。否则,位域就成了一个没有任何作用的东西了。

那么pin.t_4 = ((~pin.b_1) && (~pin.b_2));为什是错误的呢??
和  pin.t_4 = ((~pin.b_1) & (~pin.b_2));相比你会发现二者仅有一点不同,也就是&与&&。

我分别读了他们的汇编代码,发现:
其实两个式子都正确的把位域取反并相与的结果求了出来,并存储在了临时整数(类型提升产生的)的
最低位。——pin.t_4 = ((~pin.b_1) && (~pin.b_2))并不是真得这样做了,而是实际效果是相当于这样子做了。
所不同的是,在条件判断的时候,&&直接把这个整数用于是否为0的判断,而&则把这个整数的最低一位
提取出来,并用提取出来的值是否为0进行判断。至于提取最低位的手段,那肯定是对整数"&0x01"

使用特权

评论回复
54
XIANSir|  楼主 | 2011-3-1 12:29 | 只看该作者
52# 123jj
谢谢,通过这次我一定会汲取教训,注意这些事情的。

使用特权

评论回复
55
hyqfwq| | 2011-3-1 12:30 | 只看该作者
确实是的,这样的算法还是不错的

使用特权

评论回复
56
XIANSir|  楼主 | 2011-3-1 12:32 | 只看该作者
51# 123jj
呵呵,初来乍到,都不认识,希望各位多多关照。

您太谦虚了,你上面的回答是最直接最正确的。

使用特权

评论回复
57
zjaoing123| | 2011-3-1 13:03 | 只看该作者
新手上路,大家以后多多指点啦!:loveliness:

使用特权

评论回复
58
123jj| | 2011-3-1 13:14 | 只看该作者
50# ayb_ice
50# ayb_ice

“首先使用位域一定会提升,而使用~的结果是对字节操作接着使用&,结果仍是字节,最后再把字节转换成位域,这样肯定达不到LZ想要的结果”

前辈错了:
首先你上面描述的等式是:
pin.t_ ...
XIANSir 发表于 2011-3-1 12:25



ayb_ice老师没错,是小辈您错啦~~~

建议LZ再去重温一下C语言基础,搞清“!,&&,||”和“~,&,|”的本质区别。

“!,&&,||”是对逻辑结果判断并处理,即俺们常说的“1”和“0”,“对”与“错”之判断与处理。
“~,&,|”是按位对数值进行运算,两者的用途不同,尽管有时两者的结果一致,但不能证明你的方法就是正确的!并没有可移植性。

假如你搞通了上述两者的使用场合及用法,就不会提下面这个不是问题的问题了。
但是我还是有一个疑问,为什么这样也得不到正确的结果呢:
pin.t_4 = ((pin.t_1=~pin.b_1) && (pin.t_2=~pin.b_2));


如LZ一定要这样用,改写成如下形式,加入强制数据(bit)类型转换,结果一定正确。什么原因请LZ仔细想想,俺相信,LZ很聪明,一点就通~~~
pin.t_4 = ((pin.t_1=~(bit)pin.b_1) && (pin.t_2=~(bit)pin.b_2));

使用特权

评论回复
59
123jj| | 2011-3-1 13:29 | 只看该作者
俺没有匠人那么好的文采,不会说话,假如你要结果的话,对53楼的例子,结论很简单。

对“!,&&,||”之类型操作,数据一定要先转换成对应数据格式(自动转换或手工指定),才能对逻辑结果作出正确判断与处理。
pin.t_4 = ((~pin.b_1) & (~pin.b_2));    正确
pin.t_4 = ((~pin.b_1) && (~pin.b_2));  错误
pin.t_4 = ((~(bit)pin.b_1) && (~(bit)pin.b_2));  正确
pin.t_4 = ((!pin.b_1) && (!pin.b_2));  正确,C语言标准推荐用法

使用特权

评论回复
60
123jj| | 2011-3-1 13:37 | 只看该作者
LS发言漏写了对pin.t_1,pin.t_2的赋值,结果一样!

C语言内部操作是,用中间计算结果(~pin.b_1) , (~pin.b_2) 先对pin.t_1,pin.t_2赋值,再用中间计算结果求出最终结果,对pin.t_4赋值。

使用特权

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

本版积分规则