cowboy 发表于 2011-3-1 13:37:35

相同的代码,不同的结果

程序运行后,变量 c 的结果是多少?

void main(void)
{
   unsigned char a,b,c;
   a = 0x55;
   b = 0xaa;
   if (a == ~b)
   {
   c = 0;
   }
   else
   {
   c = 1;
   }

   while(1);
}

Gorgon_Meducer 发表于 2011-3-1 13:40:29

这个貌似和编译器有关系的……
未经验证,但是猜测貌似有的编译器会把~b计算为0xFF55

cowboy 发表于 2011-3-1 13:49:17

回复【1楼】 Gorgon Meducer 傻孩子
------------------------------------------
是啊,刚被这问题搞晕了。在Keil C51中 ~b 被认为是 0x55, 在IAR for 430 中被认为是 0xff55。
为什么没有统一的标准呢?

eworker 发表于 2011-3-1 13:58:40

回复【1楼】Gorgon Meducer 傻孩子
-----------------------------------------------------------------------

切中要害

tiantian2011 发表于 2011-3-1 14:00:23

都不会出现这种情况.楼上两人想错了.

~b肯定是0x55,如果哪个编译器是误认为0xff55,哪就是编译器的bug
注意,这里b被定义为char

所以结果c = 0

xielihong 发表于 2011-3-1 14:09:35

~b 如果按标准的话,应该是0xffffffaa
位运算时,先转换为int,再进行运算。

所以,与位运算有关的,都要自己强制转换。

MISRC标准里面有一条规则是讲到这点的。

little_Monkey 发表于 2011-3-1 14:10:38

楼上,在PC或者其他32位系统里面的一般char是16位的

STM32W108 发表于 2011-3-1 14:18:09

回复【2楼】cowboy
-----------------------------------------------------------------------
是啊,刚被这问题搞晕了。在Keil C51中 ~b 被认为是 0x55, 在IAR for 430 中被认为是 0xff55。
为什么没有统一的标准呢?

------------------------------------
IAR是对的。KEIL是错的。

lbc___ 发表于 2011-3-1 15:49:01

回复【2楼】cowboy
回复【1楼】 gorgon meducer 傻孩子
------------------------------------------
是啊,刚被这问题搞晕了。在keil c51中 ~b 被认为是 0x55, 在iar for 430 中被认为是 0xff55。
为什么没有统一的标准呢?
-----------------------------------------------------------------------

51是8位机,430是16位机.....不过还是很奇怪的说

shark 发表于 2011-3-1 22:17:23

这个问题 [上官金虹]很有研究,见这里,还有这里

dengting 发表于 2011-3-1 22:39:54

回复【4楼】tiantian2011
都不会出现这种情况.楼上两人想错了.
~b肯定是0x55,如果哪个编译器是误认为0xff55,哪就是编译器的bug
注意,这里b被定义为char
所以结果c = 0
-----------------------------------------------------------------------

同意,又不是int
char 就是8bit,int才与机器有关

dengting 发表于 2011-3-1 22:44:24

keil是对的专业,iar你选对目标机类型没有??
不管是16位机,还是32位机,char就应该是8bit.

cowboy 发表于 2011-3-1 23:10:11

回复【9楼】shark
-----------------------------------------------------------------------
谢谢,和我的问题很相似。


回复【10楼】dengting 守望者
-----------------------------------------------------------------------
我一开始的观点与你相同,认为变量为8位,取反后也应该是8位。
网上一些资料:
A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion.

在运算表达式中,char会提升为int再进行运算,这样看来IAR是对的,KEIL是错的,但是,KEIL这么多年了,不至于有这样的错误吧。

dengting 发表于 2011-3-11 00:13:07

OK,先记住你的话,但暂时还不相信!

millwood0 发表于 2011-3-11 05:16:20

"(a==~b)"

the relevant section in C is type promotion. essentially, any char type is promoted to int before any operations. int in this case is a word type.

so in the case above, b is first converted to int, then ~ is applied to that int type, and converted back to a char type before a comparison is made with a.

in this case, the compiler should conclude that a equals ~b.

millwood0 发表于 2011-3-11 05:17:37

"楼上,在PC或者其他32位系统里面的一般char是16位的"

char / short / long are defined in C as 8/16/32 bit types.

unfortunately, int is undefined in C, is thus implementation specific.

banyai 发表于 2011-3-11 08:01:52

Keil C51对标准C的扩充和改动非常大,在中断在改变的全局变量不需要用volatile就是一个例子。char一定是8位的,short只是规定不能大于int的位数,long不能小于int的位数。它们和int一样,由编译器决定位数。

CC2530 发表于 2011-3-11 08:29:27

这种问题已经说过N遍了.

根据标准C,char本质上没有算术与逻辑运算.
所有char的算术与逻辑运算,将自动提升为int,或者unsigned int的算术与逻辑运算.
(这叫整型提升).

对于~运算符号,标准C要求操作数至少为int.(其他运算符如+,-,*,/都有类似规定)
换句话说,char根本就不能直接进行~运算操作.

但是,char硬是要参与运算,改如何处理呢?
标准C采用变通的办法,自动把char提升至int,或者unsigned int,满足运算符的操作数的要求,
这样char才能参与运算.

如果int能表示所有char的范围,则char将提升至int.
如果不能,则提升至unsigned int.

通常int的位数比char多,int能表示所有char的范围,所以一般char都会提升至int.
但是也有例外,如果char的位数与int相同,且char是无符号,int就无法表示所有char的范围,
那么char将提升至unsigned int.

通常情况,int位数>char的位数,有:
char a;
~a的结果就是~((int)a)的结果.
除了KEIL C51(非常不遵守标准的C编译器),其他编译器如IAR,GCC,VC,C++Builder,KEIL MDK都遵守这条规则.

xieguangye 发表于 2011-3-11 09:01:03

这个就是没有按MISRA C标准编程出现的 各种貌似问题的问题。

下列来自MISRA C规范

*位运算中符号的改变:当位运算符应用在无符号短整型时,整数提升会有某些特别不利
的反响。比如,在一个unsigned char 类型的操作数上做位补运算通常会产生其值为负的
(signed)int 类型结果。在运算之前,操作数被提升为int 类型,并且多出来的那些高位
被补运算置1。那些多余位的个数,若有的话,依赖于int 的大小,而且在补运算后接右
移运算是危险的。

规则10.5(强制): 如果位运算符 ~ 和 << 应用在基本类型为unsigned char 或unsigned
short 的操作数,结果应该立即强制转换为操作数的基本类型。

则 6.1(强制): 单纯的 char 类型应该只用做存储和使用字符值。

规则6.2(强制): signed char 和unsigned char 类型应该只用做存储和使用数字值。
有三种不同的 char 类型:(单纯的)char、unsigned char、signed char。unsigned char 和signed
char 用于数字型数据,char 用于字符型数据。单纯char 类型的符号是实现定义的,不应依赖。
单纯 char 类型所能接受的操作只有赋值和等于操作符(=、==、!=)。

规则 6.4(强制): 位域只能被定义为unsigned int 或singed int 类型。

楼主的代码应该写成
if (a == (( unsigned char)(~b)))// 强制类型转换
或者
if (a == (0xff&(~b)))// 取有效位

xiaosars 发表于 2011-3-11 09:06:22

mark

CC2530 发表于 2011-3-11 09:08:39

一句话:char没有算术与逻辑运算.

cowboy 发表于 2011-3-11 09:20:46

谢谢 【17楼】 CC2530 上官金虹 ,看来是被KEIL C51的非标准编译误导了。

shark 发表于 2011-3-12 13:26:49

上官每学一样东西,就注_册一个马甲(void_c, atmega32,stm32 ...),从0积分开玩,现在得有10个马甲了吧,呵。

flagyan 发表于 2011-3-12 19:01:28

确实是值得注意的一个问题。

gujiaf 发表于 2011-3-12 19:19:15

还真不知道有这回事

mcu_001 发表于 2011-3-12 19:51:39

学习了.但如果8位机严格按c的标准,效果将大幅下降.

Pony279 发表于 2011-11-5 20:03:36

MARK个~
不太喜欢这样的C标准...

CHENXIAOTIAN 发表于 2011-11-5 22:12:56

mark 整型提升

dakeda 发表于 2011-11-5 23:49:44

这个值得学习一下

sasa_spirit 发表于 2011-11-7 20:14:41

char类型不是8位的吗?
那么unsigned char应该也是8位,
用调试器估计可以看明白什么问题

s-t-m-3-2 发表于 2012-2-22 10:29:58

2楼LOGO,有周润发的气质

yusufu 发表于 2012-2-22 10:44:22

mark~~学习了~~~

hamipeter 发表于 2012-5-4 23:07:37

学习了,以后编程要注意

liuchangkui 发表于 2012-5-5 00:52:59

mark    应该更新了

monkerman 发表于 2012-9-13 13:20:47

学习了! 过瘾!!!{:lol:}

sco518 发表于 2012-9-13 13:35:11

这个我觉得是跟你用 编译器设置的单片机有关,如果是16位的单片机,它内部的运算都是16位的,即使你定义的是uchar,它还是要占用一个int 的空间,运算也是。   所以编译器也必须要保持这样的习惯去处理,就成了0xff55了。
而keil编51,自然就按51的习惯来编译了啊。

fjourdev 发表于 2012-9-13 13:41:20

很不懂C,却有想学C,可看得头大!!!{:shy:}

armstrong 发表于 2014-12-23 19:15:16

听闻cowboy是个高手,翻到这个帖子了。2011年的啊,看得出高手的成长历程。现在来看这个问题,不再是问题了:整型提升规则。

zhenghe 发表于 2015-1-22 10:10:50

挖坟,查cowboy的帖子翻到的。整型提升,标准C要求,上个月(去年)刚明白,落后4年啊

shange144k8 发表于 2016-3-29 15:01:02

学习了.....

RichardKo 发表于 2020-12-3 08:40:57

学习了,感谢
页: [1]
查看完整版本: 相同的代码,不同的结果