2 C++ 基本内置类型
数据类型是程序的基础:它告诉我们数据的意义以及我们能在数据上执行的操作。
C++ 定义了一套包括算术类型和空类型在内的基本数据类型。其中算术类型包含了字符、整型数、布尔值和浮点数。空类型不对应具体的值,仅用于一些特殊场合,例如最常见的是,当函数不返回任何值时使用空类型作为返回类型。
1 算术类型
算术类型分为两类:整型和浮点型。其中整型又包括字符、布尔类型和整型数在内。
算术类型的尺寸(也就是该类型数据所占的比特数)在不同机器上有所差别。下表列出了 C++ 标准规定的尺寸最小值,同时允许编译器赋予这些类型更大的尺寸。
表 1:C++ 算术类型 | ||
---|---|---|
类型 | 含义 | 最小尺寸 |
bool | 布尔类型 | 未定义 |
char | 字符 | 8 位 |
wchar_t | 宽字符 | 16 位 |
char16_t | Unicode 字符 | 16 位 |
char32_t | Unicode 字符 | 32 位 |
short | 短整型 | 16 位 |
int | 整型 | 16 位 |
long | 长整型 | 32 位 |
long long | 长整型 | 64 位 |
float | 单精度浮点数 | 6 位有效数字 |
double | 双精度浮点数 | 10 位有效数字 |
long double | 扩展精度浮点数 | 10 位有效数字 |
基本的字符类型是char
,一个char
的空间应确保可以存放机器基本字符集中任意字符对应的数字值。也就是说,一个char
的大小和一个机器字节一样。其它字符类型用于扩展字符集。类型wchar_t
用于确保可以存放机器最大扩展字符集中的任意一个字符,类型char16_t
和char32_t
则为 Unicode 字符集服务。
整型用于表示不同尺寸的整数。C++ 语言规定一个int
至少和一个short
一样大,一个long
至少和一个int
一样大,一个long long
至少和一个long
一样大。
浮点型可表示单精度、双精度和扩展精度的值。C++ 标准指定了一个浮点数有效位数的最小值,然而大多数编译器都实现了更高的精度。通常,float
以 1 个字(32 比特)来表示,double
以 2 个字(64 比特)来表示,long double
以 3 或 4 个字(96 或128 比特)来表示。一般来说,类型float
和double
分别有 7 和 16 个有效位;类型long double
则常常被用于有特殊浮点需求的硬件,它的具体实现不同,精度也各不相同。
除去布尔型和扩展的字符型之外,其它整型可以划分为带符号的和无符号的两种。带符号类型可以表示正数、负数或 0,无符号类型则仅能表示大于等于 0 的值。
类型int
、short
、long
、long long
都是带符号的,通过在这些类型名前添加unsigned
就可以得到无符号类型,例如unsigned long
。类型unsigned int
可以缩写为unsigned
。
与其它整型不同,字符型被分为了三种:char
、signed char
、unsigned char
。特别需要注意的是:类型char
和类型signed char
并不一样。尽管字符型有三种,但是字符的表现形式却只有两种:带符号的和无符号的。类型char
实际上会表现为上述两种形式中的一种,具体是哪一种由编译器决定。
无符号类型中所有比特都用来存储值,例如,8 比特的unsigned char
可以表示 0 至 255 区间内的值。
2 类型转换
当在程序的某处我们使用了一种类型而其实对象应该取另一种类型时,程序会自动进行类型转换。当我们像下面这样把一种算术类型的值赋给另外一种类型时:
bool b = 42; // b 为 true
int i = b; // i 的值为 1
i = 3.14; // i 的值为 3
double pi = i; // pi 的值为 3.0
unsigned char c = -1; // 假设 char 占 8 比特,c 的值为 255
signed char c2 = 256; // 假设 char 占 8 比特,c2 的值是未定义的
类型所能表示的值的范围决定了转换的过程:
- 当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为 0 则结果为
false
,否则结果为true
。 - 当我们把一个布尔值赋给非布尔类型时,初始值为
false
则结果为 0,初始值为true
则结果为 1。 - 当我们把一个浮点数赋给整数类型时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。
- 当我们把一个整数值赋给浮点类型时,小数部分记为 0。如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型标识数值总数取模后的余数。例如,8 比特大小的
unsigned char
可以表示 0 至 255 区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对 256 取模后所得的余数。因此,把 -1 赋给 8 比特大小的unsigned char
所得的结果是 255。 - 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的。此时,程序可能继续工作、可能奔溃,也可能生成垃圾数据。
当程序的某处使用了一种算术类型的值而其实所需的是另一种类型的值时,编译器同样会执行上述的类型转换。例如,如果我们使用了一个非布尔值作为条件,那么它会被自动地转换为布尔值,这一做法和把非布尔值赋给布尔变量时的操作完全一样:
int i = 42;
if (i)
i = 0;
尽管我们不会故意给无符号对象赋一个负值,却可能写出这么做的代码。例如,当一个算术表达式中既有无符号数又有int
值时,那个int
值就会转换成无符号数。把int
转换成无符号数的过程和把int
直接赋给无符号变量一样:
unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // 输出 -84
std::cout << u + i << std::endl; // 如果 int 占 32 位,输出 4294967264
在第二个输出表达式里,相加前首先把整数 -42 转换成无符号数。把负数转换成无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数的模。
当从无符号数中减去一个值时,不管这个值是不是无符号数,我们都必须确保结果不能是一个负值:
unsigned u1 = 42, u2 = 10;
std::cout << u1 - u2 << std::endl; // 正确:输出 32
std::cout << u2 - u1 << std::endl; // 正确:不过结果是取模后的值
3 字面值常量
一个形如 42 的值被称作字面值常量。字面值常量的形式和值决定了它的数据类型。
3.1 整数和浮点型字面值
我们可以将整型字面值写作十进制数、八进制数或十六进制数的形式。以0
开头的整数代表八进制数,以0x
或0X
开头的代表十六进制数。
整型字面值具体的数据类型由它的值和符号决定。默认情况下,十进制字面值是带符号数,八进制和十六进制字面值既可能是带符号的也可能是无符号的。十进制字面值的类型是int
、long
、long long
中尺寸最小的那个,当然前提是这种类型要能容纳下当前的值。八进制和十六进制字面值的类型是能容纳其数值的int
、unsigned int
、long
、unsigned long
、long long
、unsigned long long
中尺寸最小者。如果一个字面值连与之关联的最大的数据类型都放不下,将产生错误。类型short
没有对应的字面值。
尽管整型字面值可以存储在带符号数据类型中,但严格来说,十进制字面值不会是负数。如果我们使用了一个形如 -42 的负十进制字面值,那个负号并不在字面值之内,它的作用仅仅是对字面值取负值而已。
浮点型字面值表现为一个小数或以科学计数法表示的指数,其中指数部分用E
或e
标识。默认的,浮点型字面值是一个double
。
3.2 字符和字符串字面值
由单引号括起来的一个字符称为char
型字面值,双引号括起来的零个或多个字符则构成字符串型字面值。
'a' // 字符字面值
"Hello World!" // 字符串字面值
字符串字面值的类型实际上是由常量字符构成的内置数组。编译器在每个字符串的结尾处添加一个空字符('\0'
),因此,字符串字面值的实际长度要比它的内容多 1。例如,字面值'A'
表示的就是单独的字符 A,而字符串"A"
则代表了一个字符的内置数组,该内置数组包含两个字符:一个是字母 A,另一个是空字符。
如果两个字符串字面值位置紧邻且仅由空格、缩进和换行符分隔,则它们实际上是一个整体。当书写的字符串字面值比较长,写在一行里不太合适时,就可以采取分开书写的方式:
// 分多行书写的字符串字面值
std::cout << "a really, really long string literal "
"that spans two lines" << std::endl;
3.3 转义序列
有两类字符,程序员不能直接使用:一类是不可打印的字符,如退格或其它控制字符,因为它们没有可视的图符;另一类是在 C++ 中有特殊含义的字符(单引号、双引号、问号、反斜线)。在这些情况下需要用到转义序列。转义序列均以反斜线作为开始,C++ 最常用的转义序列就是换行符\n
。
在程序中,转义序列被当作一个字符使用:
std::cout << '\n'; // 转到新一行
3.4 布尔字面值和指针字面值
true
和false
是布尔类型的字面值。nullptr
是指针字面值。