我试着执行以下程序:
#include <stdio.h>
int main() {
signed char a = -5;
unsigned char b = -5;
int c = -5;
unsigned int d = -5;
if (a == b)
printf("\r\n char is SAME!!!");
else
printf("\r\n char is DIFF!!!");
if (c == d)
printf("\r\n int is SAME!!!");
else
printf("\r\n int is DIFF!!!");
return 0;
}
对于这个计划,我得到的输出:
char是DIFF! int是SAME!
为什么我们得到两个不同的输出?
如果输出是如下?
焦炭一样! int是SAME!
一个键盘连接 。
这是因为,在C的各个隐式类型转换规则有其中两个,一个C程序员必须知道: 通常的算术转换和整数促销 (后者是前者的一部分)。
在炭情况下,你有类型(signed char) == (unsigned char)
。 这些都是小的整数类型 。 其他如小整数类型是bool
和short
。 整数提升规则指出,当一个小的整数类型是操作的操作数,它的类型将得到提升,以int
,这是签署。 如果类型为带符号会发生这种情况没有关系。
在的情况下signed char
,该标志将被保留,这将提升为int
包含值-5。 在的情况下unsigned char
,它包含一个值,该值是251(0xFB的才能)。 这将提升为int
包含相同的值。 你结束了
if( (int)-5 == (int)251 )
在整数的情况下,你有类型(signed int) == (unsigned int)
。 他们是不小的整数类型,所以整促销活动不适用。 取而代之的是,它们由通常的算术转换 ,这指出,如果两个操作数具有相同的“等级”(尺寸),但不同的符号类型,已签名的操作数转换为相同的类型无符号的一个平衡。 你结束了
if( (unsigned int)-5 == (unsigned int)-5)
酷的问题!
该int
比较有效,因为这两个整数包含完全相同的位,所以他们基本上是相同的。 但关于什么char
S'
嗯,C含蓄地促进char
s到int
S于各种场合。 这是其中之一。 您的代码表示, if(a==b)
但什么编译结果实际上是对是:
if((int)a==(int)b)
(int)a
为-5,但(int)b
是251.这些是绝对不一样的。
编辑:由于@碳酸酸指出, (int)b
是251仅当一个char
是8位长。 如果int
是32位长, (int)b
是-32764。
REDIT:有意见讨论答案的性质,如果一个字节是不是8位长的一大堆。 在这种情况下,唯一的区别是(int)b
不是251但具有不同的正数,这是不-5。 这并不是这仍然是非常酷的真正的问题有关。
欢迎到整型提升 。 如果我可以从网站引用:
如果int可以表示原始类型的所有值,该值被转换为int; 否则,它被转换为一个unsigned int。 这些被称为整数促销。 所有其它类型在整数提升不会改变。
C可让人有些困惑,当你做比较,如这些,我最近有些摸不着头脑与下面的挑逗我的非C编程的朋友:
#include <stdio.h>
#include <string.h>
int main()
{
char* string = "One looooooooooong string";
printf("%d\n", strlen(string));
if (strlen(string) < -1) printf("This cannot be happening :(");
return 0;
}
这的确不打印This cannot be happening :(
而且似乎表明,25是小于-1!
然而什么情况下是-1表示为无符号整数,由于底层的比特表示等于4294967295 32位系统。 自然25比4294967295较小。
如果我们不过显式转换size_t
通过返回类型strlen
作为一个有符号整数:
if ((int)(strlen(string)) < -1)
然后,它会比较对25 -1和所有将与世界很好。
一个好的编译器应该提醒你注意一个无符号和符号整数之间的比较,但它依然是那么容易错过(特别是如果你不启用警告)。
这是因为所有的基本类型有签名的Java程序员特别混乱。 以下是詹姆斯·高斯林(Java的创始人之一) 不得不话要说 :
高斯林:我作为一个语言设计者,我真的不认为我自己这些天,有什么“简单”真的结束了的意思是我能想到J.随机开发持有该规范在他的头上。 这个定义说,例如,Java是没有 - 事实上很多这些语言结了大量的边界情况,事情没有人真正理解。 测验任何C开发者关于无符号,很快你会发现,几乎没有C语言开发人员真正了解发生的事情与签名,未签名什么是算术。 这样的事情使C复合物。 的Java语言的部分是,我认为,很简单。 该库必须抬起头来。
的十六进制表示-5
是:
- 8位二进制补码
signed char
: 0xfb
- 32位二进制补码
signed int
: 0xfffffffb
当您将符号数转换为一个无符号数,或反之,则编译器......恰恰没有。 有什么呢? 数量或者是转换或没有,在这种情况下,不确定的或实现定义如下(我没有去查看它)和最有效的实现定义的行为是什么也不做。
所以,的十六进制表示法(unsigned <type>)-5
是:
- 8位,
unsigned char
: 0xfb
- 32位,
unsigned int
: 0xfffffffb
看起来熟悉? 他们是位对位相同签名的版本。
当你写if (a == b)
其中a
和b
是类型的char
,读什么书,编译器实际上需要的是if ((int)a == (int)b)
(这是一个“整型提升”其他人都被敲打一下。)
所以,当我们转换会发生什么char
来int
?
- 8位
signed char
到32位signed int
: 0xfb
- > 0xfffffffb
- 嗯,这是有道理的,因为它的表示相匹配
-5
以上! - 它被称为“符号扩展”,因为它复制字节的“签位”,向左进入新的,更广泛的价值的最高位。
- 8位
unsigned char
为32位signed int
: 0xfb
- > 0x000000fb
- 这一次,它做了“零扩展”,因为源类型是无符号的 ,所以没有符号位复制。
所以, a == b
确实0xfffffffb == 0x000000fb
=>不匹配!
而且, c == d
确实0xfffffffb == 0xfffffffb
=>比赛!
我的观点是:没有你在编译时警告“比较有符号和无符号表情”?
编译器试图告诉你,他有权做疯狂的事情! :)我想补充的,疯狂的事情会发生使用大值,接近原始类型的能力。 和
unsigned int d = -5;
绝对分配大值d,这相当于(即使,可能不保证当量)为:
unsigned int d = UINT_MAX -4; ///Since -1 is UINT_MAX
编辑:
然而,有趣的是,发现只有第二比较给出一个警告(查看代码) 。 因此,这意味着应用转换规则编译器确信不会有在之间的比较误差unsigned char
和char
(比较过程中它们将被转换为一个类型,可以安全地表示它的所有可能的值)。 他是对的这一点。 然后,它通知你,这将不会是的情况下unsigned int
和int
:所述比较所述2的一个将被转换为一个类型,不能完全代表它期间。
为了完整起见, 我检查它也简称 :编译器以同样的方式比字符的行为,并如市场预期,也有在运行时没有错误。
。
与此相关的话题,最近,我问这个问题 (然而,C ++面向)。