[TOC] 第7章 基本类型程序员
请别搞错:计算机处理的是数而不是符号。咱们用对行为的算术化程度来衡量咱们的理解力(和控制力)。编程
到目前为止,本书只使用了C语言的两种基本(内置的)类型:int和float。(咱们还见到过_Bool
,那是C99中的一种基本类型。)本章讲述其他的基本类型,并从整体上讨论了与类型有关的重要问题。7.1节展现整数类型的取值范围,包括长整型、短整型和无符号整型。7.2节介绍double类型和long double类型,这些类型提供了更大的取值范围和比float类型更高的精度。7.3节讨论char(字符)类型,这种类型将用于字符数据的处理。7.4节解决重要的类型转换问题,即把一种类型的值转换成另一种类型的等价值。7.5节展现利用typedef定义新类型名的方法。最后,7.6节描述sizeof运算符,这种运算符用来计算一种类型须要的存储空间大小。函数
C语言支持两种根本不一样的数值类型:整数类型(也称整型)和浮点类型(也称浮点型)。整数类型的值是整数,而浮点类型的值则可能还有小数部分。整数类型又分为两大类:有符号型和无符号型。spa
有符号整数和无符号整数code
有符号整数若是为正数或零,那么最左边的位(符号位)为0;若是是负数,则符号位为1。所以,最大的16位整数的二进制表示形式是0111111111111111,对应的值是32 767(即$2^{15}-1$)。而最大的32位整数是01111111111111111111111111111111,对应的数值是2 147 483 647(即$2^{31}-1$)。不带符号位的整数(最左边的位是数值的一部分)的整数称为无符号整数。最大的16位无符号整数是65 535(即$2^{16}-1$),而最大的32位无符号整数是4 294 967 295(即$2^{32}-1$)。ci
默认状况下,C语言中的整数变量都是有符号的,也就是说最左位保留为符号位。若要告诉编译器变量没有符号位,须要把它声明成unsigned类型。无符号整数主要用于系统编程和底层与机器相关的应用。第20章将讨论无符号整数的常见应用,在此以前,咱们一般回避无符号整数。开发
C语言的整数类型有不一样的尺寸。int类型一般为32位,但在老得CPU上多是16位。有些程序所需的数很大,没法以int类型存储,因此C语言还提供了长整型。某些时候,为了节省空间,咱们会指示编译器以比正常存储小的空间来存储一些数,称这样的数为短整型。cmd
为了使构造的整数类型正好知足要求,能够指明变量是long类型或short类型,singed类型或unsigned类型,甚至能够把说明符组合起来(如long unsigned int)。然而,实际上只有下列6种组合能够产生不一样的类型:编译器
short int unsigned short int int unsigned int long int unsigned long int
其余组合都是上述某种类型的同义词。(例如,除非额外说明,不然全部整数都是有符号的。所以,long signed int和long int是同样的类型。)另外,说明符的顺序没什么影响,因此unsigned short int和short unsigned int是同样的。it
C语言容许经过省略单词int来缩写整数类型的名字。例如,unsigned short int能够缩写为unsigned short,而long int则能够缩写为long。C程序员常常会省略int;一些新出现的基于C的语言(包括Java)甚至不容许程序员使用short int或long int这样的名字,而必须写成short或long。基于这些缘由,本书在单词int无关紧要的状况下一般将其省略。
6种整数类型的每一种所表示的取值范围都会根据机器的不一样而不一样,可是有两条全部编译器都必须遵照的原则。首先,C标准要求short int、int和long int中的每一种类型都要覆盖一个肯定的最小取值范围(详见23.2节)。其次,标准要求int类型不能比short int类型短,long int类型不能比int类型短。可是,short int类型的取值范围有可能和int类型的范围是同样的,int类型的取值范围也能够和long int的同样。
表7-1说明了在16位机上整数类型一般的取值范围,注意short int和int有相同的取值范围。
表7-1 16位机的整数类型
类型 | 最小值 | 最大值 |
---|---|---|
short int | -32 768 | 32 767 |
unsigned short int | 0 | 65 535 |
int | -32 768 | 32 767 |
unsigned int | 0 | 65 535 |
long int | -2 147 483 648 | 2 147 483 647 |
unsigned long int | 0 | 4 294 967 295 |
表7-2说明了32位机上整数类型一般的取值范围,这里的int和long int有着相同的取值范围。
表7-2 32位机的整数类型
类型 | 最小值 | 最大值 |
---|---|---|
short int | -32 768 | 32 767 |
unsigned short int | 0 | 65 535 |
int | -2 147 483 648 | 2 147 483 648 |
unsigned int | 0 | 4 294 967 295 |
long int | -2 147 483 648 | 2 147 483 647 |
unsigned long int | 0 | 4 294 967 295 |
最近64位的CPU逐渐流行起来了。表7-3给出了64位机上(尤为是在UNIX系统下)整数类型常见的取值范围。
类型 | 最小值 | 最大值 |
---|---|---|
short int | -32 768 | 32 767 |
unsigned short int | 0 | 65 535 |
int | -2 147 483 648 | 2 147 483 648 |
unsigned int | 0 | 4 294 967 295 |
long int | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 808 |
unsigned long int | 0 | 18 446 744 073 709 551 615 |
再强调一下,表7-一、表7-2和表7-3中给出的取值范围不是C标准强制的,会随着编译器的不一样而不一样。对于特定的实现,肯定整数类型范围的一种方法是检查<limits.h>头(23.2节)。该头是标准库的一部分,其中定义了表示每种整数类型的最大值和最小值的宏。
C99提供了两个额外的标准整数类型:long long int和unsigned long long int。增长这两种整数类型有两个缘由,一是为了知足日益增加的对超大型整数的需求,而是为了适应支持64位运算的新处理器的能力。这两个long long类型要求至少64位宽,因此long long int类型值得范围一般为$-2^{63}$(-9 223 372 036 854 775 808)到$2^{63}-1$(9 223 372 036 854 775 807),而unsigned long long int类型值得范围一般为0到$2^{64}-1$(18 446 744 073 709 551 615)。
C99中把short int、int、long int和long long int类型(以及signed char类型(7.3节))称为标准有符号整型,而把unsigned short int、unsigned int、unsigned long int和unsigned long long int类型(以及unsigned char类型(7.3节)和_Bool
类型(5.2节))称为标准无符号整型。
除了标准的整数类型之外,C99标准还容许在具体实现时定义扩展的数类整型(包括有符号和无符号的)。例如,编译器能够提供有符号和无符号的128位整数类型。
如今把注意力转向常量——在程序中以文本形式出现的数,而不是读、写或计算出来的数。C语言容许用十进制(基数为10)、八进制(基数为8)和十六进制(基数为16)形式书写整数常量。
八进制数和十六进制数
八进制数是用数字0~7书写的。八进制数的每一位表示一个8的幂(这就如同十进制数的每一位表示10的幂同样)。所以,八进制的数237表示成十进制数就是$28^2+38^1+7*8^0=128+24+7=159$。
十六进制数是用数字0~9加上字母A ~ F表示10~15的数。十六进制数的每一位表示一个16的幂,十六进制数1AF的十进制数值是$116^2+1016^1+15*16^0=256+160+15=431$。
请记住八进制和十六进制只是书写数的方式,它们不会对数的实际存储方式产生影响。(整数都是以二进制形式存储的,跟表示方式无关。)任什么时候候均可以从一种书写方式切换到另外一种书写方式,甚至能够混合使用:10+015+0x20的值为55(十进制)。八进制和十六进制更适用于底层程序的编写,本书直到第20章才会较多地用到它们。
十进制整数常量的类型一般为int,但若是常量的值大得没法存储在int型中,就用long int 类型。若是出现long int 不够用的罕见状况,编译器会用unsigned long int 做最后的尝试。肯定八进制和十六进制常量的规则略有不一样:编译器会依次尝试int、unsigned int、long int和unsigned long int类型,直至找到能表示该常量的类型。
为了强制编译器把常量做为长整数来处理,只需在后边加上一个字母L(或l):
15L 0377L 0x7fffL
为了指明是无符号常量,能够在常量后边加上字母U(或u):
15U 0377U 0x7fffU
L和U能够结合使用,以代表常量既是长整型又是无符号的:0xffffffffUL
。(字母L、U的顺序和大小写无所谓。)
在C99中,以LL或ll(两个字母大小写要一致)结尾的整数常量是long long int型的。若是在LL或ll的前面或后面增长字母U(或u),则该整数常量为unsigned long long int型。
C99肯定整数常量类型的规则与C89有些不一样。对于没有后缀(U、u、L、l、LL、ll)的十进制常量,其类型是int、long int或long long int中能表示该值的“最小”类型。对于八进制或者十六进制常量,可能的类型顺序为int、unsigned int、long int、unsigned long int、long long int和unsigned long long int。常量后面的任何后缀都会改变可能类型的列表。例如,以U(或u)结尾的常量类型必定是unsigned int、unsigned long int 和unsigned long long int中的一种,以L(或l)结尾的十进制常量类型必定是long int或long long int中的一种。若是常量的数值过大以致于不能用标准的整数类型表示,则可使用扩展的整数类型。
对整数执行算术运算时,其结果有可能由于太大而没法表示。例如,对两个int值进行算术运算时,结果必须仍然能用int类型来表示;不然(表示结果所需的数位太多)就会发生溢出。
整数溢出时的行为要根据操做数是有符号型仍是无符号型来肯定。有符号整数运算中发生溢出时,程序的行为是未定义的。回顾4.4节的介绍可知,未定义行为的结果是不肯定的。最可能的状况是,仅仅是运算的结果出错了,但程序也有可能崩溃,或出现其余意想不到的情况。
无符号整数运算过程当中发生溢出时,结果是有定义的:正确答案对$2^n$取模,其中n是用于存储结果的位数。例如,若是对无符号的16位数65 535加1,其结果能够保证为0。
假设有一个程序由于其中一个int变量发生了“溢出”而没法工做。咱们的第一反应是把变量的类型从int变为long int。但仅仅这样作事不够的,咱们还必须检查数据类型的改变对程序其余部分的影响,尤为是须要检查该变量是否用在printf函数或scanf函数的调用中。若是用了,须要改变调用中的格式串,由于%d只适用于int类型。
读写无符号整数、短整数和长整数须要一些新的转换说明符。
unsigned int u; scanf("%u", &u);/* reads u in base 10 */ printf("%u", u);/* writes u in base 10 */ scanf("%o", &u);/* reads u in base 8 */ printf("%o", u);/* writes u in base 8 */ scanf("%x", &u);/* reads u in base 16 */ printf("%x", u);/* writes u in base 16 */
程序:数列求和(改进版)
6.1节编写了一个程序对用户输入的整数数列求和。该程序的一个问题就是所求出的和(或其中某个输入数)可能会超出int型变量容许的最大值。若是程序运行在整数长度为16位的机器上,可能会发生下面的状况:
This program sums a series of integers. Enter integers (0 to terminate):10000 20000 30000 0 The sum is: -5536
求和的结果应该为60 000,但这个值不在int型变量表示的范围内,因此出现了溢出。当有符号整数发生溢出时,结果是未定义是未定义的,在本例中咱们获得了一个毫无心义的结果。为了改进这个程序,能够把变量改为long型。
/** * Sums a series of numbers (using long int variables) */ #include <stdio.h> int main() { long n, sum = 0; printf("This program sums a series of integers.\n"); printf("Enter integers (0 to terminate): "); scanf("%ld", &n); while (n != 0) { sum += n; printf("The sum is: %ld\n", sum); scanf("%ld", &n); if (n < 0) { printf("Length Overflow!!!"); break; } } return 0; }
这种改变很是简单:将n和sum声明为long型变量而不是int型变量,而后把scanf和printf函数中的转换说明由%d改成%ld。
整数类型并不适用于全部应用。有些时候须要变量能存储带小数点的数,或者能存储极大数或极小数。这类数能够用浮点(因小数点是“浮动的”而得名)格式进行存储。C语言提供了3种浮点类型,对应三种不一样的浮点格式。
当精度要求不严格时,(例如,计算带一位小数的温度),float类型是很适合的类型。double提供更高的进度,对绝大多数程序来讲都够用了。long double支持极高精度的要求,不多会用到。
C标准没有说明float、double和long double类型提供的精度究竟是多少,由于不一样的计算机能够用不一样方法存储浮点数。大多数现代计算机都遵循IEEE 754标准(即IEC 60559)的规范,因此这里也用它做为一个示例。
IEEE浮点标准
由IEEE开发的IEEE标准提供了两种主要的浮点数格式:单精度(32位)和双精度(64位)。数值以科学计数法的形式存储,每个数都由三部分组成:符号、指数和小数。指数部分的位数说明了数值的可能大小程度,而小数部分的位数说明了精度。单精度格式中,指数长度为8位,而小数部分占了23位。所以,单精度数能够表示的最大值大约是$3.40*10^{38}$,其中精度是6个十进制数字。
IEEE标准还描述了另外两种格式:单扩展精度和双扩展精度。标准没有指明这些格式中的位数,但要求单扩展精度类型至少为43位,而双精度类型至少为79位。为了得到更多有关IEEE标准和浮点算术的信息,能够参阅David Goldberg在1991年3月发布的“What every computer scientist should know about floating-point arithmetic”(ACM Computing Surveys, vol, 23, no.1: 5-48 )。
表7-4给出了根据IEEE标准实现时浮点类型的特征。(表中给出了规范化的最小正值,非规范化的数(23.4节)能够更小。)long double 类型没有显示在此表中,由于它的长度随着机器的不一样而变化,而最多见的大小是80位和128位。
类型 | 最小正值 | 最大值 | 精度 |
---|---|---|---|
float | $1.17549*10^{-38}$ | $3.40282*10^{38}$ | 6个数字 |
double | $2.225 07*10^{-308}$ | $1.79769*10^{308}$ | 15个数字 |
在不遵循IEEE标准的计算机上,表7-4是无效的。事实上,在一些机器上,float能够有和double相同的数值集合,或者double能够有和long double相同的数值集合。能够在头<float.h>
(23.1节)中找到定义浮点类型特征的宏。
在C99中,浮点类型分为两种:一种是实浮点类型,包括float、double和long double类型;另外一种是C99新增的复数类型(27.3节,包括float _Complex、double _Complex和long double _Complex)。