2.2.2 C语言中的整数及其相互转换
C语言中支持多种整数类型。无符号整数在C语言中对应unsigned short、unsigned int(unsigned)、unsigned long等类型,常在数的后面加一个“u”或“U”表示无符号整型常量,例如,12345U、0x2B3Cu等都属于无符号整型常量。带符号整数在C语言中对应short、int、long等类型。
C语言标准规定了每种数据类型的最小取值范围,例如,int型数据至少应为16位,取值范围为-32 768~32 767,int型数据具体的取值范围由ABI规范规定。通常,short型数据总是16位;int型数据在16位机器中为16位,在32位和64位机器中都为32位;long型数据在32位机器中为32位,在64位机器中为64位;long long型数据是在ISO C99中引入的,规定它必须是64位。
小贴士
C语言是由贝尔实验室的Dennis M.Ritchie最早设计并实现的。为了使UNIX操作系统得以推广,1977年Dennis M.Ritchie发表了不依赖于具体机器的C语言编译文本《可移植的C语言编译程序》。1978年Brian W.Kernighan和Dennis M.Ritchie合著出版了The C Programming Language,使C语言成为目前世界上使用最广泛的高级程序设计语言之一。
1988年,随着微型计算机的日益普及,出现了许多C语言版本。由于没有统一的标准,这些C语言版本之间出现了一些不一致的地方。为了改变这种情况,美国国家标准学会(ANSI)为C语言制定了一套ANSI标准,对最初贝尔实验室的C语言做了重大修改。Brian W.Kernighan和Dennis M.Ritchie编写的The C Programming Language:Second Edition对ANSI C做了全面的描述,该书被公认为是关于C语言的最好的参考手册之一。
国际标准化组织(ISO)接管了对C语言标准化的工作,在1990年推出了几乎和ANSI C一样的版本,称为ISO C90。该组织在1999年又对C语言做了一些更新,称为ISO C99,该版本引进了一些新的数据类型,对英语以外的字符串本文提供了支持。
C语言中允许无符号整数和带符号整数之间的转换,转换前、后的机器数不变,只是转换前、后对其解释方式发生了变化。转换后数的真值是将原二进制机器数按转换后的数据类型重新解释得到。例如,若将一个以1开头的机器数从带符号整型转换为无符号整型,则对它的解释从一个负数变为大于等于2n-1的大正数。由于上述原因,程序在某些情况下会发生意想不到的结果。例如,考虑以下C代码:
这里x为带符号整数,u为无符号整数,初值为2 147 483 648(231)。函数printf()用来输出数值,指示符%u、%d分别用来以无符号整数和带符号整数的形式输出十进制数的值。当在32位机器上运行上述代码时,输出结果如下。
x的输出结果说明如下:整数-1的补码表示为11…1,当作为32位无符号数解释(格式符为%u)时,其值为232-1=4 294 967 296-1=4 294 967 295。
u的输出结果说明如下:231的无符号数表示为100…0,当被解释为32位带符号整数(格式符为%d)时,其值为-232-1=-231=-2 147 483 648,即最小负数(参见例2.12,这里n=32)。
C语言标准规定,若位宽相同的无符号整数和带符号整数同时参加运算,则按无符号整数进行运算,因而结果可能不符合直觉。
例2.20 在有些32位系统上,C表达式“-2147483648<2147483647”的执行结果为false,与事实不符;但如果定义一个变量“int i=-2147483648;”,表达式“i<2147483647”的执行结果却为true。试分析产生上述结果的原因。如果将表达式写成“-2147483647-1 <2147483647”,则结果会怎样呢?
解:该问题在ISO C90标准下会出现,如图2.2a所示,编译器在处理常量时会按int32_t(int、long)、uint32_t(unsigned int、unsigned long)、int64_t(long long)、uint64_t(unsigned long long)的顺序确定数据类型,0~231-1为32位带符号整型,231~232-1为32位无符号整型,232~263-1为64位带符号整型,263~264-1为64位无符号整型。
编译器处理C表达式“-2147483648 < 2147483647”时,首先将“-2147483648”中的2147483648=231看成32位无符号整型,其机器数为0x8000 0000,然后,对其取负(按位取反,末位加1),结果仍为0x8000 0000,并作为无符号整型常数,因而该条件表达式实际上是将0x8000 0000与0x7FFF FFFF按照无符号整数比较,结果为false。在计算机内部,真正进行的是对机器数0x8000 0000和0x7FFF FFFF做减法,然后按照无符号整型比较其大小。
编译器在处理“int i=-2147483648;”时进行了类型转换,将等号右边的“-2147483648”按带符号整数赋给变量i,其机器数还是0x8000 0000,但是按带符号整数解释其值为-2 147 483 648,执行“i<2147483647”时,按照带符号整型比较,结果是true。在计算机内部,实际上是对机器数0x8000 0000和0x7FFF FFFF按照带符号整型进行比较。
对于“-2147483647-1<2147483647”,编译器首先将2 147 483 647=231-1(机器数为0x7FFF FFFF)看成带符号整型,对其取负,得到-2 147 483 647(机器数为0x8000 0001),然后将其减1,得到-2 147 483 648,与2 147 483 647比较,得到结果为true。在计算机内部,实际上是对机器数0x8000 0000和0x7FFF FFFF按照带符号整型进行比较。
图2.2 C语言中整数常量的类型
在ISO C99标准下,C表达式“-2147483648 < 2147483647”的执行结果为true。因为该标准下,如图2.2b所示,编译器在处理常量时会按int32_t(int、long)、int64_t(long long)、uint64_t(unsigned long long)的顺序确定数据类型,0~231-1为32位带符号整型,231~263-1为64位带符号整型,263~264-1为64位无符号整型。表达式中小于号左边的常数2147483648值为231,在231~263-1之间,属于64位带符号整数型,其机器数为0x0000 0000 8000 0000,执行取负操作后机器数为0xFFFF FFFF 8000 0000;右边的常数2147483647的值为231-1,属于32位带符号整型,提升为long long型后机器数为0x0000 0000 7FFF FFFF,两个数按64位带符号整型比较,结果为true。