章节目录

数字系统(十进制、二进制、十六进制和八进制)

本节阅读量:

在日常生活中,我们使用十进制数进行计数,其中每个数字可以是0、1、2、3、4、5、6、7、8或9。十进制也称为“基数10”,因为有10个可能的数字(0到9)。在这个系统中,我们这样计数:0、1、2、3、4、5、6、7、8、9、10、11……默认情况下,C++程序中的数字假设为十进制。

1
int x { 12 }; // 12 默认是十进制

在二进制中,只有2个数字:0和1,因此它被称为“基数2”。在二进制中,我们这样计数:0,1,10,11,100,101,110,111…

十进制和二进制是数字系统的两个例子。C++中有4种主要的数字系统。按流行程度排序,它们是:十进制(以10为基数)、二进制(以2为基数),十六进制(以16为基数)和八进制(以8为基数)。


八进制和十六进制

八进制是以8为基数——也就是说,唯一可用的数字是:0、1、2、3、4、5、6和7。在八进制中,我们这样计数:0,1,2,3,4,5,6,7,10,11,12,…(注意:没有8和9,所以我们从7跳到10)。

十进制 0 1 2 3 4 5 6 7 8 9 10 11
八进制 0 1 2 3 4 5 6 7 10 11 12 13

要使用八进制字面值,请在前面加上0(零):

1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    int x{ 012 }; // 0 开头的数据在C++中代表八进制
    std::cout << x << '\n';
    return 0;
}

该程序打印:

1
10

为什么是10而不是12?因为默认情况下数字以十进制输出,12八进制 = 10十进制。

八进制几乎不被使用,我们建议您避免使用它。

十六进制是以16为基数。在十六进制中,我们这样计数:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12…

十进制 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
十六进制 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11

要使用十六进制字面值,请在前面加上0x。

1
2
3
4
5
6
7
8
#include <iostream>

int main()
{
    int x{ 0xF }; // 0x 开头的数据在C++中代表十六进制
    std::cout << x << '\n';
    return 0;
}

该程序打印:

1
15

因为十六进制数字有16个不同的值,所以一个十六进制数字包含4个bit位。因此,可以使用一对十六进制数字来精确表示整个字节。

考虑值为0011 1010 0111 1111 1001 1000 0010 0110的32位整数。由于数字的长度和重复性,这不容易阅读。在十六进制中,相同的值为:3A7F 9826,这更简洁。因此,十六进制值通常用于表示内存地址或内存中的原始数据(其类型未知)。


二进制字面值

在C++14之前,不支持二进制字面值。十六进制文本为我们提供了一种有用的解决方法(您可能仍然会在现有的代码库中看到):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
    int bin{};
    bin = 0x0001; // 将二进制 0000 0000 0000 0001 设置给变量
    bin = 0x0002; // 将二进制 0000 0000 0000 0010 设置给变量
    bin = 0x0004; // 将二进制 0000 0000 0000 0100 设置给变量
    bin = 0x0008; // 将二进制 0000 0000 0000 1000 设置给变量
    bin = 0x0010; // 将二进制 0000 0000 0001 0000 设置给变量
    bin = 0x0020; // 将二进制 0000 0000 0010 0000 设置给变量
    bin = 0x0040; // 将二进制 0000 0000 0100 0000 设置给变量
    bin = 0x0080; // 将二进制 0000 0000 1000 0000 设置给变量
    bin = 0x00FF; // 将二进制 0000 0000 1111 1111 设置给变量
    bin = 0x00B3; // 将二进制 0000 0000 1011 0011 设置给变量
    bin = 0xF770; // 将二进制 1111 0111 0111 0000 设置给变量

    return 0;
}

在C++14中,可以通过使用0b前缀来使用二进制字面值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <iostream>

int main()
{
    int bin{};        // 假设是16 bit的int
    bin = 0b1;        // 将二进制 0000 0000 0000 0001 设置给变量
    bin = 0b11;       // 将二进制 0000 0000 0000 0011 设置给变量
    bin = 0b1010;     // 将二进制 0000 0000 0000 1010 设置给变量
    bin = 0b11110000; // 将二进制 0000 0000 1111 0000 设置给变量

    return 0;
}

数字分隔符

由于长文本可能很难读取,C++14还增加了使用引号(’)作为数字分隔符的功能。

1
2
3
4
5
6
7
8
9
#include <iostream>

int main()
{
    int bin { 0b1011'0010 };  // 将二进制 1011 0010 设置给变量
    long value { 2'132'673'462 }; // 阅读起来更加容易

    return 0;
}

还要注意,分隔符不能出现在值的第一个数字之前:

1
    int bin { 0b'1011'0010 };  // error: ' 在第一个数字前出现

数字分隔符只影响阅读体验,不会以任何方式影响对应的值。


以十进制、八进制或十六进制输出值

默认情况下,C++以十进制输出值。然而,您可以通过使用std::dec、std::oct和std::hex I/O操纵器来更改输出格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>

int main()
{
    int x { 12 };
    std::cout << x << '\n'; // 十进制 (默认)
    std::cout << std::hex << x << '\n'; // 16进制
    std::cout << x << '\n'; // 现在是16进制
    std::cout << std::oct << x << '\n'; // 八进制
    std::cout << std::dec << x << '\n'; // 重新设置回十进制
    std::cout << x << '\n'; // 十进制

    return 0;
}

这将打印:

1
2
3
4
5
6
12
c
c
14
12
12

请注意,一旦设置好之后,I/O操纵器将保持为设置的输出格式,直到再次更改。


输出二进制值

以二进制格式输出值有点困难,因为std::cout没有内置此功能。幸运的是,C++标准库包含一个名为std::bitset的类型,它将为我们完成这项工作(在头文件中)。

要使用std::bitset,我们可以定义一个std::bitset变量,并告诉std::bitset要存储多少位。位数必须是编译时常量。可以用整数值(以任何格式,包括十进制、八进制、十六进制或二进制)初始化std::bitset。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <bitset> // 引入 std::bitset
#include <iostream>

int main()
{
	// std::bitset<8> 意味着要存储 8 个 bit
	std::bitset<8> bin1{ 0b1100'0101 }; // 二进制的 1100 0101
	std::bitset<8> bin2{ 0xC5 }; // 十六进制的 1100 0101

	std::cout << bin1 << '\n' << bin2 << '\n';
	std::cout << std::bitset<4>{ 0b1010 } << '\n'; // 创建一个临时的bitset并打印

	return 0;
}

这将打印:

1
2
3
11000101
11000101
1010

在上述代码中,此行:

1
std::cout << std::bitset<4>{ 0b1010 } << '\n'; // 创建一个临时的bitset并打印

创建临时(未命名)的4位的std::bitset对象,用二进制字面值0b1010对其进行初始化,以二进制格式打印值,然后丢弃临时对象。

在C++20和C++23中,通过新的格式库(C++20)和打印库(C++23),我们有更好的选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <format> // C++20
#include <iostream>
#include <print> // C++23

int main()
{
    std::cout << std::format("{:b}\n", 0b1010); // C++20
    std::cout << std::format("{:#b}\n", 0b1010); // C++20

    std::print("{:b} {:#b}\n", 0b1010, 0b1010); // C++23

    return 0;
}

这将打印:

1
2
3
1010
0b1010
1010 0b1010

5.3 字面值常量

上一节

5.5 条件运算符

下一节