2.4 基本类型与数据表示
C语言提供了一组基本数据类型,并规定了“类型名”。基本类型的名字由一个或几个标识符(关键字)构成,其形式与前面讲的“名字”有所不同。本节将介绍几个常用的类型。
首先应提出常量的概念。常量就是程序里直接写出的数据,例如,程序里直接写出的整数类型的数据就称为“整型常量”。为简单起见,也常把整型常量简称为“整数”,其他情况也采用类似称呼方式,后面常用这种简称,只在特别需要时才用更严格的说法。C语言规定了各种基本类型的常量的书写形式,这也是本节的主要内容。
2.4.1 整数类型和整数的表示
C语言提供了多个整数类型以适应不同需要。不同整数类型间的差异在于它们可能具有不同的二进制编码位数,因此表示范围可能不同。程序中用的最多是一般整数类型(今后简称为“整数类型”或“整型”)和长整数类型(简称“长整型”)。整数类型的类型名是“int”,长整型的类型名为“long int”,可简写为“long”。int和long都是关键字。
1.整数表示
整数(int类型的常量)有几种书写形式,程序中的整数一般采用十进制写法。用十进制方式写出的一个整数就是普通数字字符组成的一个连续序列,其中不能有空格、换行或其他字符。C语言规定十进制表示的数字序列的第一个字符不能是0,除非要写的整数本身就是0。下面是一些整数的例子。
123 304 25278 1 0 906
由于长整数是另一个不同类型的整数,所以C语言为长整数规定了一种专门写法,其特殊之处是在表示数值的数字序列最后附一个字母“l”或“L”作后缀。由于小写字母“l”容易与数字“1”混淆,建议读者采用大写的“L”。下面是一些长整数的例子。
123L 304l 25278L 1l 0L 906L
2.表示范围
C语言没有规定各种整数类型的表示范围,也就是说,没有规定各种整数的二进制编码长度。对于int和long,只规定了long类型的表示范围不小于int,但也允许它们的表示范围相同。具体C语言系统则会对整型和长整型规定表示方式和表示范围。例如,早期计算机的一些C语言系统采用16位二进制表示的整数(一个int占2个字节)和32位表示的长整数(一个long占4个字节)。这样,整型的表示范围就是-32768~32767,即-215~215-1。长整型的表示范围是-231~231-1。在许多新的计算机C语言系统里,整数(int)和长整数(long int)都采用32位的二进制数表示。
C语言允许在整数的前面写正负号,加上负号的整数就是负整数。
3.整数的八进制书写法和十六进制书写法
整数与长整数都可以采用八进制或十六进制的形式书写。
用八进制形式写出的整数(int类型的常量)是由数字0开始的连续数字序列,在序列中只允许0~7这8个数字。下面是用八进制写法写出的一些整数和长整数。
0236 0527 06254 0531 0765432L
整数的十六进制形式是由0x或0X开头的数字序列。由于数字只有10个,而在十六进制写法中需要16个数字,C语言采用计算机领域通行的方式,用字母a~f 或A~F 表示其余的6个十六进制数字,其对应关系是:
下面是用十六进制形式写出的一些整数和长整数。
0x2073 0xA3B5 0XABCD 0XFFFF 0XF0F00000L
注意:八进制、十进制和十六进制只是整数的不同书写形式,提供多种写法是为了编程方便,使人可以根据需要选择适用的书写方式。无论采用八进制写法还是十六进制写法,写出的仍是某个整数类型的数,并不是新的类型。用八进制、十六进制形式写长整数时,同样需要加后缀l或者L。
日常生活中人们习惯于用十进制的形式书写整数。C语言提供八进制和十六进制的整数书写方式,也是为了写程序的需要。在写复杂程序时,有些情况下用八进制和十六进制更方便些,后面会看到这方面的例子。
4.整型变量
用于存放整型数据的变量称为整型变量,如果定义了一个整型变量i:
int i; i=10;
则变量i在内存中存放形式如下:
数值在内存中是以二进制补码表示的:
● 正数的补码和原码相同;
● 负数的补码是将该数的绝对值的二进制形式按位取反再加1。
例如:求-10的补码。
10的原码:
取反:
再加1,得-10的补码:
由此可知,左面的第一位是表示符号的。
整型变量可以分为以下几种类型。
基本整型:类型说明符为int,在内存中占2个字节。
短整型:类型说明符为short int或short,所占字节和取值范围均与基本型相同。
长整型:类型说明符为long int或long,在内存中占4个字节。
以上整型变量按照有无符号还可以分为有符号类型(signed)和无符号类型(unsigned)。所以C语言共有6种整型变量。
表2-1列出了各类整型变量所分配的内存字节数及数的表示范围。
表2-1 整型类型的有关数据
注意:要用某计算机上的某个C语言系统编程时,要做的一件事就是查清该系统里各种整数类型的表示范围。有关情况可以从系统使用手册中查到,或查看介绍该系统的书籍,或查看系统的联机帮助,此外还可以查看这个C语言系统中名字为limit.h的文件。这是每个C语言系统都有的一个标准文件,其中列出了各种情况的具体规定,对于接下来介绍的浮点数也有类似情况。例如,在一些C语言系统里,long double采用与double同样的表示方式。有关具体C语言系统中浮点数表示的情况,也应查阅系统手册,还可以查阅名为float.h的标准文件。
5.整型变量的定义
变量定义的一般形式为:
类型说明符 变量名标识符,变量名标识符,…;
例如:
int a, b, c; (a, b, c为整型变量) long x, y; (x, y为长整型变量) unsigned p, q; (p, q为无符号整型变量)
在书写变量定义时,应注意以下几点。
(1)允许在一个类型说明符后定义多个相同类型的变量。各变量名之间用逗号间隔,类型说明符与变量名之间至少用一个空格间隔。
(2)最后一个变量名之后必须以“; ”号结尾。
(3)变量定义必须放在变量使用之前,一般放在函数体的开头部分。
【例2.2】整型变量的定义与使用。
main() { int a, b, c, d; unsigned u; a=12; b=-24; u=10; c=a+u; d=b+u; printf("a+u=%d, b+u=%d\n", c, d); }
运行结果是:
a+u=22, b+u=-14
在本例中可以看出,不同种类的整型数据可以进行算术运算。
6.整型数据的溢出
一个整型变量所能表示的最大值是32767,如果再加1,系统将无法正确表示,也就是说计算结果超出了范围,人们将这种情况称为“溢出”。
【例2.3】整型数据的溢出。
main() { int a, b; a=32767; b=a+1; printf("%d, %d\n", a, b); }
在这里,人们期望程序运行后变量b的结果是32768,但是实际输出却是:
32 767,-32 768
因为内存中32767和-32768这两个数的表示形式分别是:
这就像车辆的机械式里程表(当然在计算机内存中是二进制的)达到最大值后又从最小值开始计数。
2.4.2 实数类型和实数的表示
1.实数类型
C语言提供了3个表示实数的类型:单精度浮点数类型,简称浮点类型,类型名为float;双精度浮点数类型,简称双精度类型,类型名为double;长双精度类型,类型名为long double。这些类型的常量也分别称作“浮点数”“双精度数”和“长双精度数”。所有整数类型和实数类型统称为算术类型。
实数在计算机内部的表示由具体系统规定,其中不少系统采用通行的国际标准(IEEE标准,IEEE是电子电气工程师协会,是一个著名的国际性技术组织)。
(1)浮点类型的数用4个字节32位二进制表示,这样表示的数大约有7位十进制有效数字,数值的表示范围约为±(3.4×10-38~3.4×1038)。
(2)双精度类型的数用8个字节64位二进制表示,双精度数大约有16位十进制有效数字,数值的表示范围约为±(1.7×10-308~1.7×10308)。
(3)长双精度类型的数用10个字节80位二进制表示,大约有19位十进制有效数字,其数值的表示范围约为±(1.2×10-4932~1.2×104932)。
显然,每个实数类型能表示的数也只是数学中实数的一个子集合,不仅表示范围有限,表示的精度(数的有效数字位数)也有限,读者应注意这些情况。
2.实型常量的写法
C语言中最基本的实数类型是双精度类型。双精度数的书写形式中的基本部分是一个数字序列,在序列中或者包含了一个表示小数点的圆点“.”(可以是第一个或最后一个字符),或者在表示数值的数字序列后面有一个指数部分。指数部分是以e或E开头的另一(可以包括正负号的)数字序列,指数以10为底,这种形式称为科学记数法,也可以既有小数点,又有指数部分。下面是一些双精度数的例子。
3.2 3. 2E-3 2.45e17 0.038 105.4E-10 304.24E8
其中一些双精度类型的常量(双精度数)与它们所表示的实数的对照见表2-2。
表2-2 双精度数与实数值对照表
浮点数(float)类型的写法与双精度数类似,只是在数最后应加后缀字符f或者F。表示长双精度数的后缀是l 和L。下面是一些浮点数类型和长双精度类型数的例子。
13.2F 1.7853E-2F 24.68700f 32F 0.337f
12.869L 3.417E34L 05L 5.E88L 1.L
负实数同样通过在数前加负号表示。
3.实型数据在内存中的存放形式
实型数据一般占4个字节(32位)的内存空间,按指数形式存储。实数3.14159在内存中的存放形式如下:
● 小数部分占的位(bit)数愈多,数的有效数字愈多,精度愈高;
● 指数部分占的位数愈多,则能表示的数值范围愈大。
4.实型变量的分类
实型变量分为单精度(float型)、双精度(double型)和长双精度(long double型)3类。
实型变量定义的格式和书写规则与整型相同。
例如:
float x, y; (指定x, y为单精度实型量) double a, b, c; (指定a, b, c为双精度实型量) long double m, n; (指定m, n为长双精度实型量)
单精度型占4个字节(32位)内存空间,其数值范围为3.4E-38~3.4E+38,只能提供7位有效数字;双精度型占8个字节(64位)内存空间,其数值范围为1.7E-308~1.7E+308,可提供16位有效数字。各种实型变量的不同点见表2-3。
表2-3 各种实型变量的有关数据
5.实型数据的舍入误差
由于实型变量是由有限的存储单元组成的,因此能提供的有效数字总是有限的,如例2.4。
【例2.4】实型数据的舍入误差。
main() { float a, b; a=123456.789e5; b=a+20 printf("%f\n", a); printf("%f\n", b); }
程序的运行结果为:
12345678848.000000 12345678848.000000
程序中printf函数中的“%f”表示输出一个实数时的格式符。从程序运行结果来看,输出b的值和a的相等。原因是a的值比20大很多,a+20的理论值是12345678920,而一个float类型的变量只能保证7位有效数字,后面的数字是无意义的,并不能准确地表示该数。所以在进行实数加减法运算的时候,应尽量避免相差太悬殊的两个数直接相加或者相减,否则可能丢失小数。注意:1.0/3*3的结果并不等于1。
【例2.5】
main() { float a; double b; a=33333.33333; b=33333.33333333333333; printf("%f\n%f\n", a, b); }
程序运行结果为:
33333.332031 33333.333333
从本例可以看出,由于a是单精度浮点型,有效位数只有7位,而整数已占5位,故2位小数之后均为无效数字。b是双精度型,有效位为16位,但规定小数后最多保留6位,其余部分四舍五入。
2.4.3 字符类型和字符的表示
1.字符
字符类型数据主要用于程序的输入输出。此外,文字处理也是计算机的一个重要应用领域,该应用领域的应用程序必须能使用和处理字符形式的数据。由于大部分程序都需要与人打交道,需要接收人的输入信息(例如人给程序输入的命令,或者提供的数据),还需要给人输出信息,因此字符类型的数据在程序中的使用很广泛。
最常用的字符类型的类型名是char。字符类型的数据值包括本计算机所用编码字符集中的所有字符。目前计算机和工作站常用ASCII字符集,其中的字符包括所有大小写英文字母、数字、各种标点符号字符,还有一些控制字符,一共128个。扩展的ASCII字符集包括256个字符。字符集的所有字符都是字符类型的值。在程序执行时,其中的字符就用对应的编码表示,一个字符通常占用一个字节。
字符文字量的书写形式是用单引号括起的单个字符,例如’1''a''D’等。一些特殊字符无法这样写出,例如换行字符等,C语言为它们规定了特殊写法。几个最常用的转义字符的写法见表2-4。
表2-4 常用的转义字符及其含义
这里的写法都是在单引号里面先写一个反斜线字符(\),后面再写一个(或多个)字符。在这种写法中,反斜线字符的作用就是表明它后面的字符不取原来意义。这样连续的两个(或多个)字符称为一个转义字符,用于表示无法写出的字符。反斜线字符在其中起特殊作用。
这里需要强调两点。
(1)字符数据与标识符不同。例如,x和’x’是两种完全不同的东西,后者表示一个数据项,是程序处理的对象;前者则是程序描述中所用的一个名字,它可能代表程序里的某个东西。显然它们不在同一个层次上。
(2)数字字符和数不同。例如,1和’1',前者是一个整型文字量,是一个int类型的数据对象,其存储要占据int 所规定的单元数,在常见的计算机C语言系统里,它可能占了2个或4个字节,其中存着整数1的二进制编码;而’1’是个char 类型的数据,其存储通常占1个字节,其中存储着字符’1’的编码(在ASCII 码中’1’的编码是49)。
C语言的一个特殊规定是把字符看做是一种特别短的整数,允许程序中直接用字符的值参与算术运算。
2.字符变量
字符变量用来存储字符常量,即单个字符。
字符变量的类型说明符是char。字符变量类型定义的格式和书写规则都与整型变量相同。例如:
char a, b; (指定a、b为字符型变量) char x, y; (指定x、y为字符型变量)
3.字符数据在内存中的存储形式及使用方法
每个字符变量被分配1个字节的内存空间,因此只能存放1个字符。字符值是以ASCII码的形式存放在变量的内存单元之中的。
如,x的十进制ASCII码是120, y的十进制ASCII码是121。对字符变量a、b赋以’x'和’y’值:
a='x'; b='y';
实际上是在a、b两个单元内存放120和121的二进制代码。
a
b
所以也可以把它们看成是整型量。C语言允许对整型变量赋以字符值,也允许对字符变量赋以整型值。在输出时,允许把字符变量按整型量输出,也允许把整型量按字符量输出。
整型量为二字节量,字符量为单字节量,当整型量按字符型量处理时,只有低八位字节参与处理。
【例2.6】向字符变量赋以整数。
main() { char a, b; a=120; b=121; printf("%c, %c\n", a, b); printf("%d, %d\n", a, b); }
程序运行结果为:
x, y 120,121
本程序中定义a、b为字符型,但在赋值语句中赋以整型值。从结果看,a、b的值的输出形式取决于printf函数格式控制字符串中的格式符。当格式符为"c"时,对应输出的变量值为字符,当格式符为"d"时,对应输出的变量值为整数。
【例2.7】
main() { char a, b; a='a'; b='b'; a=a-32; b=b-32; printf("%c, %c\n%d, %d\n", a, b, a, b); }
程序运行结果为:
A, B 65,66
本例中,a、b被说明为字符变量并赋以字符值,C语言允许字符变量参与数值运算,即用字符的ASCII 码参与运算。由于大小写字母的ASCII 码相差32,因此运算后把小写字母换成大写字母,然后分别以整型和字符型输出。
4.字符串
字符串是C程序里可以直接写出来的另一类数据,其形式是用双引号括起来的一系列字符。下面是几个字符串的例子。
"CHINA" "Beijing" "Daxue" "Welcome\n"
在字符串里的特殊字符也用转义字符的形式书写,例如上面第四个字符串的最后是一个转义字符表示一个换行字符。
程序中的字符串主要用于输入输出,在第1章的简单C程序里有下面一行代码:
printf("Good morning! \n");
圆括号里就是一个字符串。
C语言规定程序不能在字符串中间换行,否则编译会出错。另外,C语言没有字符串变量。