整数是一种整型类型,可以表示正负整数,包括 0(例如 -2、-1、0、1、2)。C++ 提供了 4 种主要的、基本的整数类型供使用
类型 | 最小大小 | 注意 |
---|---|---|
short int | 16 位 | |
int | 16 位 | 在现代架构上通常为 32 位 |
long int | 32 位 | |
long long int | 64 位 |
各种整数类型之间的主要区别在于它们的大小不同——更大的整数可以容纳更大的数字。
提醒
C++ 只保证整数具有一定的最小大小,而不是特定的确切大小。有关如何在您的机器上确定每种类型的大小,请参阅课程 4.3 -- 对象大小和 sizeof 运算符。
题外话…
严格来说,bool
和 char
类型被认为是整型(因为这些类型将其值存储为整数值)。在接下来的几课中,我们将把这些类型排除在讨论之外。
有符号整数
在日常生活中书写负数时,我们使用负号。例如,-3 表示“负 3”。我们通常也会将 +3 识别为“正 3”(尽管通常的约定是我们通常省略加号前缀)。
这种为正、为负或为零的属性称为数字的符号。
默认情况下,C++ 中的整数是有符号的,这意味着数字的符号作为值的一部分存储。因此,有符号整数可以容纳正数和负数(以及 0)。
在本课程中,我们将重点关注有符号整数。我们将在下一课中讨论无符号整数(只能容纳非负数)。
定义有符号整数
以下是定义四种有符号整数类型的首选方式
short s; // prefer "short" instead of "short int"
int i;
long l; // prefer "long" instead of "long int"
long long ll; // prefer "long long" instead of "long long int"
尽管 short int、long int 或 long long int 都可以工作,但我们更喜欢这些类型的短名称(不使用 int 后缀)。除了输入更多之外,添加 int 后缀使类型更难与 int 类型的变量区分开来。如果无意中遗漏了 short 或 long 修饰符,这可能会导致错误。
整数类型还可以采用可选的 signed 关键字,根据约定,该关键字通常放在类型名称之前
signed short ss;
signed int si;
signed long sl;
signed long long sll;
然而,不应使用此关键字,因为它是多余的,因为整数默认是有符号的。
最佳实践
首选不使用 int
后缀或 signed
前缀的简写类型。
有符号整数范围
如上一节所述,一个 n 位的变量可以容纳 2n 个可能的值。但具体是哪些值呢?我们将数据类型可以容纳的特定值集合称为其范围。整数变量的范围由两个因素决定:其大小(以位为单位)以及它是否带符号。
例如,一个 8 位有符号整数的范围是 -128 到 127。这意味着一个 8 位有符号整数可以安全地存储 -128 到 127 之间的任何整数值(包括两端)。
下表包含不同大小有符号整数的范围
大小 / 类型 | 范围 |
---|---|
8 位有符号 | -128 到 127 |
16 位有符号 | -32,768 到 32,767 |
32 位有符号 | -2,147,483,648 到 2,147,483,647 |
64 位有符号 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
对于擅长数学的人来说,一个 n 位有符号变量的范围是 -(2n-1) 到 (2n-1)-1。
对于不擅长数学的人……请使用表格。:)
致进阶读者
上述范围假设采用“二进制补码”表示法。这种表示法是现代架构事实上的标准(因为它更容易在硬件中实现),并且现在是 C++20 标准所要求的。我们在课程 O.4 -- 在二进制和十进制表示之间转换整数 中讨论二进制补码。
在以前的标准中,出于历史原因允许使用符号-大小和反码表示法。这种表示法产生的值范围是 -(2n-1-1) 到 +(2n-1-1)。
溢出
如果我们将值 140 赋给一个 8 位有符号整数会发生什么?这个数字超出了 8 位有符号整数可以容纳的范围。数字 140 需要 9 位来表示(8 个幅度位和 1 个符号位),但我们在一个 8 位有符号整数中只有 8 位可用(7 个幅度位和 1 个符号位)。
C++20 标准有这样一句概括性声明:“如果在表达式求值过程中,结果在数学上未定义或不在其类型可表示值的范围内,则行为是未定义的”。通俗地说,这被称为溢出。
因此,将值 140 赋给 8 位有符号整数将导致未定义行为。
如果算术运算(例如加法或乘法)尝试创建超出可表示范围的值,则称为整数溢出(或算术溢出)。对于有符号整数,整数溢出将导致未定义行为。
#include <iostream>
int main()
{
// assume 4 byte integers
int x { 2'147'483'647 }; // the maximum value of a 4-byte signed integer
std::cout << x << '\n';
x = x + 1; // integer overflow, undefined behavior
std::cout << x << '\n';
return 0;
}
在作者的机器上,上面打印了
2147483647 -2147483648
然而,由于第二个输出是未定义行为的结果,因此您的机器上输出的值可能会有所不同。
致进阶读者
我们在课程 4.5 -- 无符号整数,以及为何要避免使用它们 中介绍无符号整数溢出时会发生什么。
通常,溢出会导致信息丢失,这几乎从来都不是我们希望的。如果有任何怀疑某个对象可能需要存储超出其范围的值,请使用范围更大的类型!
整数除法
当两个整数相除时,如果商是整数,C++ 的行为符合您的预期
#include <iostream>
int main()
{
std::cout << 20 / 4 << '\n';
return 0;
}
这产生了预期的结果
5
但是让我们看看当整数除法导致小数结果时会发生什么
#include <iostream>
int main()
{
std::cout << 8 / 5 << '\n';
return 0;
}
这产生了可能出乎意料的结果
1
当对两个整数进行除法(称为整数除法)时,C++ 总是产生一个整数结果。由于整数不能容纳小数部分,任何小数部分都会被简单地丢弃(而不是四舍五入!)。
仔细看看上面的例子,8 / 5 产生值 1.6。小数部分 (0.6) 被丢弃,剩下结果 1。或者,我们可以说 8 / 5 等于 1 余 3。余数被丢弃,剩下 1。
类似地,-8 / 5 产生值 -1。
警告
使用整数除法时要小心,因为您将丢失商的任何小数部分。然而,如果这是您想要的,整数除法是安全使用的,因为结果是可预测的。
如果需要小数结果,我们在课程 6.2 -- 算术运算符 中展示了一种实现方法。
小测验时间
问题 #1
一个 5 位有符号整数的范围是多少?
问题 #2
a) 13 / 5 的结果是什么?
b) -13 / 5 的结果是什么?