像42这样的值,在程序中被当作字面值常量:字面值是因为仅能用它的值称呼它,常量是因为它的值不能修改。每个字面值都有相应的类型,例如:0是int型,3.14159是double型。字面值仅存在于内置类型,没有类类型的字面值。因此,也没有任何标准库类型的字面值。
建议:使用内置算术类型
C++中整型数有点令人迷惑不解。就像C语言一样,C++的设计允许程序在必要时接近硬件。整型被定义成满足各种各样硬件的特性。大多数程序员可以(应该)通过限制实际使用的类型来忽略这些复杂性。
实际上,许多人用整型进行计数。例如:程序经常计算像vector或数组这种数据结构的元素个数。在第3章和第4章中,我们将看到标准库定义了一组类型用于统计对象的大小。因此,当计数这些元素时使用标准库定义的类型总是正确的。其他情况下,使用unsigned类型比较明智,可以避免值越界导致结果为负数的可能性。
当执行整型算术运算时,很少使用short类型。大多数程序中,使用short类型可能会隐含赋值越界的错误。这个错误会产生什么后果将取决于所使用的机器。比较典型的情况是值“截断(wraps around)”以至于因越界而变成很大的负数。同样的道理,虽然char类型是整型,但是char类型通常用来储存字符而不用于计算。事实上,在某些应用中char类型被当作signed类型,在另外一些应用中则被当作unsigned类型,这使得把char类型作为计算类型使用时容易出问题。
在大多数机器上,使用int类型作整型计算不易出错。就技术上而言,int类型用16位表示——对大多数应用来说太小了。实际应用中,大多数通用机器都是使用和long类型一样长的32 位来表示int类型。整型运算时,用32位表示int类型和用64位表示long类型的机器会出现选择int类型还是long类型的难题。在这些机器上,用long类型进行计算所付出的运行时代价远远高于用int类型进行同样的计算的代价,所以选择类型前要先了解程序的细节并且比较long类型与int类型的实际运行时性能代价。
决定使用哪种浮点型就容易多了:使用double类型基本上不会有错。在float类型中隐式的精度损失是不能忽视的,而double类型精度代价相对于float类型精度代价可以忽略。事实上,有些机器上,double类型比float类型的计算要快得多。long double类型提供的精度通常没有必要,且还需要承担额外的运行代价。
习题
习题2.1 int、long和short类型之间有什么差别?
习题2.2 unsigned和signed类型有什么差别?
习题2.3 如果在某机器上short类型占16位,那么可以赋给short类型的最大数是什么?unsigned short类型的最大数又是什么?
习题2.4 当给16位的unsigned short对象赋值100000时,赋的值是什么?
习题2.5 float类型和double类型有什么差别?
习题2.6 要计算抵押贷款的偿还,利率、本金和付款额应分别选用哪种类型?解释你选择的理由。
1. 整型字面值规则
定义字面值整数常量可以使用以下三种标记中的任一种:十进制、八进制和十六进制。当然这些标记不会改变其二进制位的表示形式。例如,我们能将值20定义成下列三种形式中的任意一种:
20 // decimal
024 // octal
0x14 // hexadecimal
以0(零)开头的字面值整数常量表示八进制,以0x或0X开头的表示十六进制。
字面值整数常量的类型默认为int或long类型。其精度类型决定于字面值——其值适合int就是int类型,比int大的值就是long类型。通过增加后缀,能够强制将字面值整数常量转换为long、unsigned或unsigned long类型。通过在数值后面加L或者l(字母“l”大写或小写)指定常量为long类型。
定义长整型时,使用大写字母L。小写字母l很容易和数值1混淆。
类似地,可通过在数值后面加U或u定义unsigned类型。同时加L和U就能够得到unsigned long类型的字面值常量。但其后缀不能有空格:
128u /* unsigned */ 1024UL /* unsigned long */
1L /* long */ 8Lu /* unsigned long */
没有short类型的字面值常量。
2. 浮点字面值规则
通常可以用十进制或者科学计数法来表示浮点字面值常量。使用科学计数法时,指数用E或者e表示。默认的浮点字面值常量为double类型。在数值的后面加上F或f表示单精度。同样加上L或者l表示扩展精度(再次提醒,不提倡使用小写字母l)。下面每一组字面值表示相同的值:
3.14159F .001f 12.345L 0.
3.14159E0f 1E-3F 1.2345E1L 0e0
3. 布尔字面值和字符字面值
单词true和false是布尔型的字面值:
bool test = false;
可打印的字符型字面值通常用一对单引号来定义:
'a' '2' ',' ' ' // blank
这些字面值都是char类型的。在字符字面值前加L就能够得到wchar_t类型的宽字符字面值。如:
L'a'
4. 非打印字符的转义序列
有些字符是不可打印的。不可打印字符实际上是不可显示的字符,比如退格或者控制符。还有一些在语言中有特殊意义的字符,例如单引号、双引号和反斜线符号。不可打印字符和特殊字符都用转义字符书写。转义字符都以反斜线符号开始,C++语言中定义了如下转义字符:
换行符 \n 水平制表符 \t
纵向制表符 \v 退格符 \b
回车符 \r 进纸符 \f
报警(响铃)符 \a 反斜线 \\
疑问号 \? 单引号 \‘
双引号 \"
我们可以将任何字符表示为以下形式的通用转义字符:
\ooo
这里ooo表示三个八进制数字,这三个数字表示字符的数字值。下面的例子是用ASCII码字符集表示字面值常量:
\7 (响铃符) \12 (换行符) \40(空格符)
\0 (空字符) \062 (‘2’) \115(‘M’)
字符’\0’通常表示“空字符(null character)”,我们将会看到它有着非常特殊的意义。
同样也可以用十六进制转义字符来定义字符:
\xddd
由一个反斜线符、一个x和一个或者多个十六进制数字组成。
5. 字符串字面值
之前见过的所有字面值都有基本内置类型。还有一种字面值——字符串字面值——更加复杂。字符串字面值是一串常量字符,这种类型将在4.3节详细说明。
字符串字面值常量用双引号括起来的零个或者多个字符表示。不可打印字符表示成相应的转义字符。
"Hello World!" // simple string literal
"" // empty string literal
"\nCC\toptions\tfile.[cC]\n" // string literal using newlines and tabs
为了兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符。字符字面值
'A' // single quote: character literal
表示单个字符A,然而
"A" // double quote: character string literal
表示包含两个字符的字符串:字母A和空字符。
正如存在宽字符字面值,如
L’a’
也存在宽字符串字面值,一样在前面加“L”,如
L"a wide string literal"
宽字符串字面值是一串常量宽字符,同样以一个宽空字符结束。
6. 字符串字面值的连接
两个相邻的仅由空格、制表符或换行分开的字符串字面值(或宽字符串字面值)可连接成一个新字符串字面值。这使得多行书写长字符串字面值变得简单:
// concatenated long string literal
std::cout << "a multi-line "
"string literal "
"using concatenation"
<< std::endl;
执行这条语句将会输出:
a multi-line string literal using concatenation
如果连接字符串字面值和宽字符串字面值,将会出现什么结果呢?例如:
// Concatenating plain and wide character strings is undefined
std::cout << "multi-line " L"literal " << std::endl;
结果是未定义的(undefined)——也就是说,没有定义标准的方法连接不同的类型。这个程序可能会执行,也可能会崩溃或者产生没有用的值,而且在不同的编译器下程序的动作可能不同。
建议:不要依赖未定义的操作
使用了未定义操作的程序都是错误的,如果程序能够运行,也只是巧合。未定义的操作源于编译器不能检测到的程序错误或太麻烦以至无法检测的错误。
不幸的是,含有未定义操作的程序在有些环境或编译器中可以正确执行,但并不能保证同一程序在不同编译器中甚至在当前编译器的后继版本中会继续正确运行,也不能保证程序在一组输入上可以正确运行且在另一组输入上也能够正确运行。
程序不应该依赖未定义的操作。
同样地,通常程序不应该依赖机器相关的操作,譬如假定int的位数是个固定且已知的值,这样的程序被认为是不可移植的(nonportable)。当程序移植到另一台机器上时,要寻找并更改任何依赖于机器相关的操作的代码。捕获以前可以运行的程序中的这类问题是一项非常不愉快的任务。
7. 多行字面值
处理长字符串有一个更基本的(但不常使用)方法,这个方法依赖于很少使用的程序格式化性质:在一行的末尾加一反斜线符号可将此行和下一行当作同一行处理。
正如1.4.1节提到的,C++的格式非常自由。特别是仅有一些地方不能插入空格,其中之一是在单词中间。特别是不可以在单词中间断开一行。但可以通过使用反斜线符号巧妙实现:
// ok: A \ before a newline ignores the line break
std::cou\
t << "Hi" << st\
d::endl;
等价于
std::cout << "Hi" << std::endl;
可以使用这个特性来编写长字符串字面值:
// multiline string literal
std::cout << "a multi-line \
string literal \
using a backslash"
<< std::endl;
return 0;
}
注意反斜线符号必需是该行的尾字符——不允许有注释或空格符。同样,后继行行首的任何空格和制表符都是字符串字面值的一部分。正因如此,长字符串字面值的后继行才不会有正常的缩进。
习题
习题2.7 解释下列字面值常量的不同之处。
(a) ’a’,L’a’,"a",L"a"
(b) 10,10u,10L,10uL,012,0xC
(c) 3.14,3.14f,3.14L
习题2.8 确定下列字面值常量的类型:
(a) –10 (b) -10u (c) -10. (d) -10e-2
习题2.9 下列哪些(如果有)是非法的?
(a) "Who goes with F\145rgus?\012"
(b) 3.14e1L (c) "two" L"some"
(d) 1024f (e) 3.14UL
(f) "multiple line
comment"
习题2.10 使用转义字符,编写一段程序,输出2M,然后换行。修改程序,输出2,跟着一个制表符,然后是M,最后是换行符。





