
4.4.1 字面常量
考虑下面这几个用字符序列(character sequence)的形式所书写的常量:

这四个常量在计算机内部都是用0000 0000 0100 0001这种字节流(byte stream)来表示的。由于我们在书写这些常量时添加了相关的标点(例如小数点、单引号),因此编译器可以根据这些信息推断出它们各自的类型:

这些值都是按照字面形式录入源代码的,它们的类型会由编译器根据字面写法来推断,或者说,会由编译器根据其中的相关标点来推断,因为这些标点本身就已经说明了这是个什么样的值。
这几个值在计算机内部都用二进制格式(也就是前面说到的字节流)来表示。例如65这个字面值,计算机总是会把它表示成一个二进制的整数,这个整数的值就是65。'A'这个字面值属于单个的字符,计算机会用含义跟该字符相当的二进制值来表示它。8.0这个字面值,计算机可以把它当成float(单精度浮点数)来表示,也可以把它当成double(双精度浮点数)来表示,这两种表示方式在二进制格式上稍微有一点区别。131072.0这个字面值,同样可以采用float或double来表示,如果计算机把它跟8.0都当成double来表示,那么这两个值的二进制格式就是相同的,只不过具体的取值有所区别。
下面我们再分别针对这三种常量,多举几个例子:
□整数常量(integer constant):65、1024、-17、163758、0、-1
□双精度浮点数常量(double constant):3.5、-0.7、1748.3753、0.0、-0.0000007、15e4、-58.1e-4
□字符常量(character constant):'A'、'a'、'6'、'0'、'>'、'.'、'\n'
请注意,这三种常量里面都出现了跟0有关的值,但由于类型不同,因此计算机在表示这些值时,所使用的二进制格式也不相同。整数常量0和双精度浮点数常量0.0都是数学上的零值,但字符常量'0'在数学上则不是零值,这个问题我们到第15章再讲。
另外,大家可能会注意到,双精度浮点数常量里面,有两个值的写法看上去比较奇怪,也就是15e4与-58.1e-4。这两个值用的是科学计数法(scientific notation),第一个值相当于15×104(也就是150000),第二个值相当于-58.1×10-4(也就是-0.000581)。如果某数太大或太小,用普通的小数形式写起来很麻烦,那我们就会采用科学计数法来简短地表示这个数。
如果整数常量的值太大,超出了默认类型所能表示的范围,那么编译器就会改用另一种类型来表示这个值。根据计算机的架构,编译器可能会改用long int或者long long int来表示int类型无法容纳的整数。这是一种隐式类型转换(implicit typecasting,也叫作自动类型转换)。假如编译器不这么做,那它就得把这个值塞到一个比较小的空间里面,从而导致其中的部分内容丢失。
单精度浮点数常量与双精度浮点数常量也会出现这样的情况。编译器如果发现默认的类型表示不了这个值,那就会自动选用范围更大的类型来表示它。
前面那些数字都是用以10为底的数制来表示的,或者说,都是用十进制来表示的。在这种数制下,我们可以用0至9这十种数位来表示某个数。每个十进制的数都可以表示成有10的整数次方参与的连加式,例如,2573可以表示成2000+500+70+3,然后,我们把其中的每一项都写成相应位置上的数位与10的某个整数次方相乘的形式,这样就得到:2573=2*103+5*102+7*101+3*100。
有时我们还会用以8为底或者以16为底的数制来表示某个数,这两种数制分别称为八进制(octal)与十六进制(hexadecimal)。在C语言里面,八进制数用0开头,后面的那些数位都必须是有效的八进制数位,也就是说,只能在0至7这8种数位里面挑选。十六进制数用0x或0X开头(也就是先写0,然后写字母x或X),后面的数位必须是有效的十六进制数位,也就是说,只能在0至9与a至f(或A至F)这16种数位里面挑选。
这里不打算详细讲解八进制与十六进制,仅举一些例子供大家参考:
□八进制整数:07、011、036104
□无符号的八进制整数:07u、011u、036104u
□八进制长整数:07L、011L、036104L
□十六进制整数:0x4、0Xaf、0x106a2、0x7Ca6d
□无符号的十六进制整数:0x4u、0Xafu、0x106a2u、0x7Ca6du
□十六进制长整数:0x4L、0XafL、0x106a2L、0x7Ca6dL
我们现在只需要记住:十进制、八进制、十六进制是表达同一个数值的几种方式。
另外,如果你要把某个小数明确地表示成float(单精度浮点数)类型,而不是占用空间比较大的double(双精度浮点数)类型,那么可以给这个小数的后面添加f或F。如果你想表示的是一个相当大的双精度浮点数,那么可以给浮点数的末尾添加l或L后缀。笔者建议你使用大写字母L,因为小写字母l容易跟数字1混淆。下面举几个例子:
□单精度浮点数字面量:0.5f、-12E5f、3.45e-5F
□双精度浮点数字面量:0.5、-12E5、3.45e-5
□长双精度浮点数字面量:0.5L、-12E5L、3.45e-5L
这些字面常量会由编译器来解读,并嵌入编译之后的程序中。
字面量通常有两种用途,一种是像上一节说的那样用来初始化变量,另一种则是用来对变量执行某种已知的运算,例如像下面这样,让变量与某个已知的值相除:

在刚才那两条语句中,除法的除数均为小数,因此,无论被除数是整数还是小数,整个除法的计算结果都是小数。例如,对于第一条语句来说,无论inches变量是什么类型,它与12.0相除的结果都是小数,然后程序会把这个结果转化成feet变量所属的类型,并通过赋值操作(=操作)赋给feet。与之类似,对于第二条语句来说,无论feet变量是什么类型,它与3.0相除的结果都是小数(可能是单精度浮点数,也可能是双精度浮点数),然后,程序在给yards变量赋值的时候,会把这个结果转化成yards所属的类型。