施工提示
本博文尚在施工中,内容并未完成且可能变更。
常用数据类型
本节(以及 数据类型进阶 )介绍 C++ 中的常用数据类型。它们包括:
内置字面值:空指针(nullptr)
内置变量类型:
算术类型:整型(整数、字符型、布尔型)与浮点数。
空类型(void)
标准库支持的变量类型:例如字符串(
std::string
)。标准库支持的模板类型:例如向量(
std::vector
)。
关于指针与引用,请参考 引用与指针 一节的内容。
整型:布尔、字符、整数
C++ 规定了多种类型的最小尺寸(同时允许编译器赋予更大的尺寸),详见下表:
布尔型:只允许
true
与false
两种取值。下例中的长整型或长长整型的后缀也可以使用小写字母 l 代替大写 L,但不推荐。
关键字 |
类型 |
最小尺寸 |
前后缀 |
---|---|---|---|
bool |
布尔型 |
/ |
|
字符型 |
|||
char |
字符 |
8 位 |
u8 前缀(仅字面) |
wchar_t |
宽字符 |
16 位 |
L 前缀 |
char16_t |
Unicode 16 字符 |
16 位 |
u 前缀 |
char32_t |
Unicode 32 字符 |
32 位 |
U 前缀 |
整数 |
|||
short |
短整型 |
16 位 |
|
int |
整型 |
16 位(>= short) |
|
long |
长整型 |
32 位(>= int) |
L 后缀 |
long long C++ 11 |
长长整型 |
64 位(>= long) |
LL 后缀 |
无符号整型
除了布尔与扩展字符之外的整型,可以划分为带符号(正、负或0)与无符号(不小于0)两种。
整数类型(short, int, long 与 long long)均是带符号的,在前加
unsigned
可以得到对应的无符号类型。无符号整数
unsigned int
可以直接简写为unsigned
无符号整数后缀
u/U
可以与长整型后缀L
或LL
结合,例如uL
表示无符号长整型。
字符型(char)分为 char, signed char 与 unsigned char 三种,但实际上只会表现为后两种。
char 类型会表现为带符号或无符号这两种中的一种,具体由编译器决定。
C++ 的整型类型选择
数值非负时,选用
unsigned
类型如果整数超出了
int
范围,换用long long
类型不要混用无符号与带符号类型进行算术计算
不要用 char 参与算术计算,因为 char 是无符号的还是带符号的取决于编译器
位数取值范围*
各位数对应的取值范围:
位数 |
取值范围 |
最小尺寸类型 |
---|---|---|
16 位 |
-32768 ~ 32767(带符号) |
short |
0 ~ 65535(无符号) |
unsigned short |
|
32 位 |
±2.147x10^9(带符号) |
long |
0 ~ 4.294x10^9(无符号) |
unsigned long |
|
64 位 |
±9.223x10^18(带符号) |
long long C++ 11 |
0~1.844x10^19(无符号) |
unsigned long long |
通过 <limits>
库的 numeric_limits<T>
模板及其成员,我们可以查看(在当前设备环境下的)数值类型的值占用位数、取值上限与取值下限:
std::numeric_limits<T>::digits
:类型 T 的值占用位数(带符号类型将减去符号位)std::numeric_limits<T>::lowest()
:类型 T 的取值下限std::numeric_limits<T>::max()
:类型 T 的取值上限
警告
请注意,以下取值范围只适用于编译该代码时的设备环境。以下结果仅表示,在此时的编译环境下 int 类型是 32 位的。
1#include <iostream>
2#include <limits>
3using std::cout;
4using std::endl;
5
6int main() {
7 cout << "Following results can be environment-dependent:" << endl;
8 // 输出使用无符号类型的位数,因为带符号类型的值位数要少一位(符号位)
9 cout << "int (" << std::numeric_limits<unsigned>::digits << " bits):\t\t"
10 << std::numeric_limits<int>::lowest() << ", "
11 << std::numeric_limits<int>::max() << endl;
12 cout << "short (" << std::numeric_limits<unsigned short>::digits << " bits):\t"
13 << std::numeric_limits<short>::lowest() << ", "
14 << std::numeric_limits<short>::max() << endl;
15 cout << "long long (" << std::numeric_limits<unsigned long long>::digits << " bits):\t"
16 << std::numeric_limits<long long>::lowest() << ", "
17 << std::numeric_limits<long long>::max() << endl;
18 return 0;
19}
20/*
21 输出:
22 Following results can be environment-dependent:
23 int (32 bits): -2147483648, 2147483647
24 short (16 bits): -32768, 32767
25 long long (64 bits): -9223372036854775808, 9223372036854775807
26*/
整型字面值
结合 C++ 整型尺寸 表中的前、后缀内容,我们给出一些整型字面值的例子:
u8"abc" // UTF-8 字符型字符串 "abc"
u'x' // Unicode 16 字符 'x'
-12L // 长整型数 -12
13579ULL // 无符号长长整型数 13579
此外,八进制(前缀 0)或者十六进制(前缀 0x)的数也可以用前缀标识:
20 // 普通十进制数
012 // 八进制数,前缀 0
0x2f // 十六进制数,前缀 0x
转义字符
含有特殊含义的字符无法直接使用,必须进行转义,例如换行符用 '\n'
表示。C++ 规定了以下转义字符:
转义字符 |
含义 |
转义字符 |
含义 |
转义字符 |
含义 |
---|---|---|---|---|---|
\n |
换行 |
\t |
横向制表 |
\v |
纵向制表 |
\b |
退格 |
\r |
回车 |
\f |
进纸 |
\' |
单引号 |
\" |
双引号 |
\? |
问号 |
\\ |
反斜线 |
\a |
响铃 |
转义字符被视为单个字符。
广义转义字符:使用
\x
前缀加 16 进制数字,或者使用反斜线\
加不超过 3 位的 8 进制数字组成:'\101' // 字符 A '\x41' // 字符 A "\102\12" // 字符串 "B\n"
cctype:字符操作
C++ 头文件 cctype
来源于 C 语言的标准文件 ctype.h
,并提供了一系列字符操作函数,例如:
// 对于字符变量 c
isalpha(c) // 判断是否为字母
isdigit(c) // 判断是否为数字
isxdigit(c) // 判断是否为十六进制数字
isalnum(c) // 判断是否为字母或数字
isspace(c) // 判断是否为空白符
islower(c) // 判断是否为小写字母
isupper(c) // 判断是否为大写字母
tolower(c) // 如果 c 是大写字母,转为小写
toupper(c) // 如果 c 是小写字母,转为大写
特殊字符与 Unicode*
备注
C++ 的宽字符 wchar_t
类型与宽字符串 std::wstring
类型通用性并不强,或者在跨平台时需要复杂的打印技巧(比如专用的打印函数或者 C 风格的 printf
),因此我不推荐使用。如果只需要对字符串进行打印、而不涉及到截取等字符串操作,那么我推荐直接使用简单的 std::string
。
关于字符串的基础用法,参考 字符串 一节。
我们首先看一个不那么标准的、懒人的办法,即照常使用 std::string
来存储含特殊字符的字符串,并使用 Unicode 编码来代表字符。但是,这样的存储方式 可能会使字符串的长度不等于预期,因为单个特殊字符需要的存储长度可能大于 char 类型的长度。
下面是一个 Windows 平台的示例,使用了 Windows.h
的 SetConsoleOutputCP()
来启用控制台编码支持,其中 CP_UTF8
实质上就是 65001:
如果 Unicode 字符的十六进制码不超过 4 位,使用
\u
转义前缀如果 Unicode 字符的十六进制码超过 4 位,在其左侧补 0 直到 8 位,然后使用
\U
转义前缀,
1#include <iostream>
2#include <string>
3#include <windows.h>
4using std::cout;
5using std::endl;
6
7int main() {
8 SetConsoleOutputCP(CP_UTF8); // 用于 Windows
9
10 // 使用 Unicode 码
11 std::string greek_alpha = "\u03B1"; // U+03B1
12 cout << "Alpha: " << greek_alpha << endl;
13
14 std::string mathcal_A = "Letter \U0001d49c"; // U+1D49C
15 cout << "Mathcal A: " << mathcal_A << endl;
16
17 std::string meow = "\u15E2\u1561\u14D7";
18 cout << "Meow (string): " << meow << endl;
19 if (meow.size() > 3) {
20 cout << "* unexpected size: " << meow.size() << endl;
21 }
22 return 0;
23}
24/*
25 输出:
26 Alpha: α
27 Mathcal A: 𝒜
28 Meow (String): ᗢᕡᓗ
29 * unexpected size: 9
30*/
不推荐直接在代码文件中粘贴特殊字符
在 C++ 中,最简单的使用特殊字符的方式是直接书写为字符串字面值(并建议指明 u8
),但是这种方法是不推荐的!
// 不推荐!
std::string cc = u8"你好,世界!";
std::cout << cc << std::endl;
不推荐的原因是它不稳定:
你的特殊符号被如何存储,取决于该代码文件的编码(甚至 BOM)。
他人阅读代码时,其设备上可能没有配置能正确显示特殊符号的环境。
即使能够正常显示,这种阅读也是不可靠的。因为许多符号看起来难以察觉到它们的不同,甚至同一显示符号可能对应不同的 Unicode 码。
编译器有不能恰当地处理这种文件的风险。
在不同环境(例如不同系统平台上)的编译结果可能不同。
一个从理论上来说更好的方法是使用 std::wstring
,因为它可以正确地存储符号、不会显示错误的字符串长度;但它的打印方法在 Windows 上可能十分繁琐:
1#include <iostream>
2#include <string>
3#include <windows.h>
4using std::cout;
5using std::endl;
6
7void print_wstring(std::wstring &ws, HANDLE &handle);
8
9int main() {
10 SetConsoleOutputCP(CP_UTF8); // 用于 Windows
11
12 std::wstring greek_abc = L"\u03B1\u03B2\u03B3";
13 cout << "wstring size: " << greek_abc.size() << endl;
14
15 // 一种 hack 式的 wstring 打印方式
16 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
17 print_wstring(greek_abc, handle);
18 return 0;
19}
20/* 输出:
21 wstring size: 3
22 αβγ
23*/
24
25void print_wstring(std::wstring &ws, HANDLE &handle) {
26 DWORD c;
27 if (!WriteConsoleW(handle, ws.c_str(), ws.size(), &c, nullptr)) {
28 std::cerr << "Error in wstring writing" << endl;
29 }
30}
在某些环境下,也可以利用 <codecvt>
与 <locale>
标准库将 wstring(或者 u16string, u32string)转为 UTF-8 字符串然后打印。
浮点数
我们通常使用双精度浮点数(double)而不是 float,因为 float 精度通常不足而双精度的计算效率并不明显低(甚至有时候更快)。
浮点数同样有无符号类型,例如
unsigned double
关键字 |
类型 |
最小尺寸 |
前后缀 |
---|---|---|---|
float |
单精度浮点数 |
6 位有效数字 |
后缀 f/F |
double |
双精度浮点数 |
10 位有效数字 |
|
long double |
扩展精度浮点数 |
10 位有效数字 |
后缀 L |
浮点数字面值
浮点数字面值允许科学记数法,使用字母
e
或E
如同整数一样,浮点数允许无符号后缀
u/U
结合 C++ 浮点数尺寸 表中的内容,例子:
1.2e3f // 单精度浮点数 1.2 x 10^3
3.1415926L // 扩展精度浮点数 3.1415926
2.718uL // 无符号扩展精度浮点数 2.718
空类型:void
空类型常用于无需返回值的函数定义中。下例中,函数只执行打印而不返回结果,因此定义为 void
类型:
#include <iostream>
void print_char_twice(char c) {
std::cout << c << c << std::endl;
}
int main() {
print_char_twice('a');
return 0;
}
void 也用在指针类型中,参考 void 指针* 一节。