Cpp学习6 第 4 章 基础数据类型
4.1 — 基本数据类型简介
位、字节和内存寻址
内存的最小单位是二进制数字 (也称为比特 ),它可以存储 0 或 1 的值。
内存被组织成称为内存地址 (简称地址 )的顺序单元。
每个内存地址存储一个字节的数据。一个字节是由若干比特组成的单元,可以作为一个整体进行操作。现代标准是,一个字节由 8 个连续的比特组成。
数据类型
因为计算机上的所有数据都只是一串比特,所以我们使用数据类型 (通常简称类型 )来告诉编译器如何以某种有意义的方式解释内存中的内容。
基本数据类型
C++语言提供了许多预定义的数据类型供使用。其中最基本的类型被称为基本数据类型 (有时也非正式地称为基本类型或原始类型 )。
整数类型与整型 Integer vs integral types
C++标准定义了以下术语:
- standard integer types 标准整数类型有
short、int、long、long long(包括它们的有符号和无符号变体)。 - integral types 整数类型包括
bool、各种 char 类型和标准整数类型。
“整型”一词仅包含基本类型。这意味着非基本类型(例如 enum 和 enum class )不是整型,即使它们存储为整数(在 enum 的情况下,也显示为整数)。
其他类型的集合
前两个功能是语言本身内置的(无需包含头文件即可使用):
- “基本数据类型”提供了最基本、最重要的数据类型。
- “复合数据类型”提供了更复杂的数据类型,并允许创建自定义(用户定义)类型。我们将在第 12.1 课——复合数据类型简介中介绍这些内容。
第三组(也是最大的一组)类型由 C++ 标准库提供。由于所有 C++ 发行版都包含标准库,因此这些类型广泛可用,并且为了兼容性已经过标准化。使用标准库中的类型需要包含相应的头文件并链接标准库。
在现代 C++ 中,字符串是标准库的一部分。
新版 C++ 中定义的许多类型(例如 std::nullptr_t )都使用 _t 后缀。该后缀表示“类型”,是现代类型中常用的命名法。如果看到带有 _t 后缀的内容,它很可能是一种类型。但很多类型并没有 _t 后缀,所以这个规则并没有统一应用。
4.2 — Void
void 的意思就是“无类型”。void 是我们遇到的第一个不完整类型的例子。 不完整类型是指已经声明但尚未定义的类型。编译器知道这类类型的存在,但没有足够的信息来确定应该为该类型的对象分配多少内存。
void 最常用的意思是表示函数不返回值已弃用:不接受参数的函数
void 关键字在 C++ 中还有第三种(更高级的)用法,我们将在 19.5 节—void 指针 中介绍。
4.3 — 对象大小和 sizeof 运算符
整数类型 char 、 short 、 int 、 long 和 long long 最小大小分别为 8、16、16、32 和 64 位。char 和 char8_t 正好是 1 个字节(至少 8 位)。
| Category | Type | Minimum Size | Typical Size |
|---|---|---|---|
| Boolean | bool | 1 byte | 1 byte |
| Character | char | 1 byte (exactly) | 1 byte |
| wchar_t | 1 byte | 2 or 4 bytes | |
| char8_t | 1 byte | 1 byte | |
| char16_t | 2 bytes | 2 bytes | |
| char32_t | 4 bytes | 4 bytes | |
| Integral | short | 2 bytes | 2 bytes |
| int | 2 bytes | 4 bytes | |
| long | 4 bytes | 4 or 8 bytes | |
| long long | 8 bytes | 8 bytes | |
| Floating point | float | 4 bytes | 4 bytes |
| double | 8 bytes | 8 bytes | |
| long double | 8 bytes | 8, 12, or 16 bytes | |
| Pointer | std::nullptr_t | 4 bytes | 4 or 8 bytes |
为了最大限度地提高可移植性,不应该假设对象大于指定的最小尺寸。或者,如果想假设某个类型具有非最小大小(例如,int 类型至少为 4 字节),可以使用 static_assert ,使编译器在不支持此假设的架构上编译时失败。
为了确定特定机器上数据类型的大小,C++ 提供了一个名为 sizeof 运算符。sizeof 运算符是一个一元运算符,它接受一个类型或一个变量作为参数,并返回该类型对象的大小(以字节为单位)。编译并运行以下程序来了解某些数据类型的大小:
1 |
|
对不完整类型(例如 void )使用 sizeof 将导致编译错误。
也可以对变量名使用 sizeof 运算符。但是sizeof 不包含对象使用的动态分配内存。
4.4 — 有符号整数
1 | short s; // prefer "short" instead of "short int" |
整数默认是有符号数。
n 位有符号变量的范围是 -( ) 到 ( )-1。
| Size / Type | Range |
|---|---|
| 8-bit signed | -128 to 127 |
| 16-bit signed | -32,768 to 32,767 |
| 32-bit signed | -2,147,483,648 to 2,147,483,647 |
| 64-bit signed | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
Overflow 溢出
C++20 标准明确指出:“如果在表达式求值过程中,结果在数学上未定义,或者不在其类型可表示的值范围内,则行为未定义。” 通俗地说,这被称为溢出 。
整数除法
在 C++ 中进行两个整数相除运算(称为整数除法 )时,结果始终为整数。由于整数不能包含小数部分,因此任何小数部分都会被直接舍弃(而不是四舍五入!)。
仔细观察上面的例子,8 除以 5 的结果是 1.6。小数部分 (0.6) 被舍弃,结果剩下 1。或者,我们也可以说 8 除以 5 等于 1 余 3。余数被舍弃,结果仍然是 1。同样地,-8 / 5 的结果为 -1。
4.5 — 无符号整数及避免使用的原因
无符号整数只能表示非负整数。
1 | unsigned short us; |
n 位无符号变量的范围为 0 到 。
如果无符号值超出范围,则将其除以比该类型中的最大值大 1,并仅保留余数。“模溢出”
开发者通常应该避免使用无符号整数。
无符号数更容易超出范围的底部,因为范围的底部是 0,而大多数值都接近 0。
其次,更隐蔽的是,混合使用有符号整数和无符号整数会导致意想不到的行为。在 C++ 中,如果一个数学运算(例如算术运算或比较)涉及一个有符号整数和一个无符号整数,有符号整数通常会被转换为无符号整数。因此,运算结果将是无符号的。
表示数量(即使是非负数量)和进行数学运算时,应优先使用有符号数而非无符号数。避免混用有符号数和无符号数。
什么时候应该使用无符号数呢?
- 在进行位操作时(详见第 O 章)
- 当需要明确定义的回绕行为时(例如在加密和随机数生成等算法中),无符号数也很有用。
- 处理器/内存受限的环境,出于性能原因,使用无符号数更为常见和被接受(在某些情况下,这是不可避免的)。
4.6 — 固定宽度整数和 size_t
int的大小不是固定的。
C++11 提供了一组替代的整数类型,保证在任何架构上都具有相同的大小。由于这些整数的大小是固定的,因此它们被称为定宽整数 。
std.int8_t
std:uint8_t
std:int16_t
std:uint16_t
std:int32_t
std:uint32_t
std:int64_t
std:uint64_t
当需要具有保证范围的整数类型时,请使用定宽整数类型。
由于 C++ 规范中的一个疏忽,现代编译器通常将 std::int8_t 和 std::uint8_t (以及相应的快速和最小定宽类型,我们稍后会介绍)分别视为 signed char 和 unsigned char 。因此,在大多数现代系统中,8 位定宽整数类型的行为与 char 类型相同。
快速类型(std::int_fast#_t 和 std::uint_fast#_t);最小类型(std::int_least#_t 和 std::uint_least#_t); 不推荐用
- 如果整数的大小无关紧要(例如,数字始终在 2 字节有符号整数的范围内),则优先使用
int。例如,如果要求用户输入年龄或从 1 数到 10,那么int是 16 位还是 32 位都无关紧要(数字都能容纳在 2 字节整数中)。这足以应对可能遇到的绝大多数情况。 - 存储需要保证范围的数量时,最好使用
std::int#_t。 - 当需要进行位操作或需要定义良好的回绕行为时(例如用于密码学或随机数生成),建议使用
std::uint#_t
尽量避免以下情况:
short整数和long整数(建议使用固定宽度整数类型)。- 快速和最小类型(最好使用固定宽度的整数类型)。
- 用于存储数量的无符号类型(建议使用有符号整数类型)。
- 8 位固定宽度整数类型(建议使用 16 位固定宽度整数类型)。
- 任何编译器特定的固定宽度整数(例如,Visual Studio 定义了 __int8、__int16 等……)
std::size_t 是什么?
sizeof 返回的是 std::size_t 类型的值。 是实现定义的无符号整数类型的别名, 编译器会判断 if std::size_t is an unsigned int, an unsigned long, an unsigned long long, etc…
如果你在代码中显式地使用 std::size_t ,请 #include 定义 std::size_t 头文件之一(我们推荐 <cstddef>)。使用 sizeof 不需要头文件。
4.7 — 科学计数法简介
科学计数法是一种简洁明了地表示冗长数字的有效方法。虽然科学计数法乍看之下可能有些陌生,但理解它有助于你理解浮点数的工作原理。
由于在 C++ 中输入或显示指数可能比较困难,我们使用字母“e”(有时也用“E”)来表示等式中的“乘以 10 的幂”部分。例如, 1.2 x 10⁴ 写成 1.2e4 5.9722 x 10²⁴ 写成 5.9722e24 。
关于科学计数法,最重要的一点是:有效数字(即字母“e”之前的数字)中的数字称为有效数字 (或有效位数 )。有效数字越多,数字就越精确。
4.8 — 浮点数
float 4 bytes, double 8 bytes, long double 8, 12, or 16 bytes
在现代架构中,浮点类型通常使用 IEEE 754 标准中定义的浮点格式。
避免使用 long double 。
使用浮点字面量时,务必至少包含一位小数(即使小数点为 0)。这有助于编译器理解该数字是浮点数而不是整数。
默认情况下,浮点字面量类型为 double。后缀 f 用于表示 float 类型的字面量。
务必确保字面量的类型与它们被赋值或用于初始化的变量的类型一致。否则,将会导致不必要的类型转换,并可能造成精度损失。
浮点类型只能精确表示一定数量的有效数字。使用有效数字位数超过最小限制的值可能会导致存储的值不准确。
输出浮点数时, std::cout 默认精度为 6,它会截断之后的任何内容。
可以使用名为 std::setprecision() 的 output manipulator 函数来覆盖 std::cout 显示的默认精度。 输出操纵函数会改变数据的输出方式,它们在 iomanip 头文件中定义。
1 |
|
输出操纵器(和输入操纵器)是sticky的——这意味着如果你设置了它们,它们就会保持设置状态。
舍入误差使浮点数比较变得棘手。
当数字无法精确存储时,就会出现舍入误差。即使是像 0.1 这样的简单数字也可能发生这种情况。因此,舍入误差时有发生,而且确实经常发生。舍入误差并非例外,而是常态。永远不要假设你的浮点数是精确的。
NaN 和 Inf
EEE 754 兼容格式还支持一些特殊值:
- Inf 表示无穷大。Inf 是有符号的,可以是正数 (+Inf) 或负数 (-Inf)。
- NaN ,即“非数字”的缩写。NaN 有几种不同的类型(我们这里就不讨论了)。
- 有符号零,意味着“正零”(+0.0)和“负零”(-0.0)有单独的表示形式。
4.9 — 布尔值
布尔变量是只能取两个可能值的变量: true , false 。
1 | bool b1 { true }; |
当我们打印布尔值时, std::cout 打印 0 表示 false ,打印 1 表示 true.
如果你想让 std::cout 输出 true 或 false 而不是 0 或 1 ,你可以输出 std::boolalpha 。这不会输出任何内容,但会改变 std::cout 输出 bool 值的方式。
整数到布尔值的转换
整数 0 将被转换为 false ,任何其他整数将被转换为 true 。
默认情况下, std::cin 只接受布尔变量的数值输入: 0 为 false , 1 为 true 。任何其他数值都会被解释为 true ,并导致 std::cin 进入故障模式。任何非数值都会被解释为 false ,并导致 std::cin 进入故障模式。
我们输入了 true , std::cin 静默失败并将 b 设置为 false 。因此,当 std::cout 打印 b 的值时,它打印的是 0 。
要允许 std::cin 接受 false 和 true 作为输入,必须先向 std::boolalpha 输入。
(这个 std::boolalpha 还挺麻烦的)
4.10 — if 语句简介
if else 无需多言。
if (x) 表示“如果 x 非零/非空”。
当与 if 语句结合使用时,提前返回提供了一种对函数返回值进行条件化的方法。
4.11 — 字符
char 数据类型设计用于存储单个 character 。一个字符可以是单个字母、数字、符号或空格。
ASCII 代表美国信息交换标准代码,它定义了一种用 0 到 127 之间的数字(称为 ASCII 码或代码点 )来表示英文字符(以及一些其他符号)的特定方法。
使用 std::cout 打印字符时,std::cout 会将 char 变量输出为 ASCII 字符。
用 std::cin.get() 函数来执行提取操作,不会忽略前导空格。
C++ 中有一些字符序列具有特殊含义,这些字符被称为转义序列 。转义序列以反斜杠 () 开头,后面跟着一个字母或数字。
最常见的转义序列: '\n' ,它可以用来打印换行符;另一个常用的转义序列是 '\t' ,它嵌入了一个水平制表符
转义序列以反斜杠 (\) 开头,而不是正斜杠 (/)。
避免使用多字符字面量。
Unicode 和本地化通常不在本教程的讨论范围内,因此不再赘述。
4.12 — 类型转换和 static_cast 简介
Implicit type conversion 隐式类型转换
当编译器在未经我们明确指示的情况下自动进行类型转换时,我们称之为隐式类型转换 。
由于将浮点值转换为整数值会导致小数部分被丢弃,因此编译器会在执行从浮点值到整数值的隐式类型转换时发出警告。
通过 static_cast 运算符进行显式类型转换
1 |
|
有符号和无符号之间的转换
如果要转换的值无法用目标类型表示:
- 如果目标类型为无符号整数,则该值将被取模。我们将在第 4.5 课—无符号整数及其避免使用原因 中讲解取模。
- 如果目标类型是有符号的,则在 C++20 之前,该值是实现定义的,从 C++20 开始,该值将被模运算包装。
4.x — 第四章总结和测验
遵循最佳实践,每个函数应该只做一件事。
我把计算高度和输出塞到一个函数里了….
- 标题: Cpp学习6 第 4 章 基础数据类型
- 作者: 铁名_IronName
- 创建于 : 2026-08-07 16:28:19
- 更新于 : 2026-08-08 09:45:38
- 链接: https://blog.ironname.top/2026/Cpp/Cpp学习6/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。