4.4 — 有符号整数

整数是一种整型类型,可以表示正负整数,包括 0(例如 -2、-1、0、1、2)。C++ 提供了 4 种主要的、基本的整数类型供使用

类型最小大小注意
short int16 位
int16 位在现代架构上通常为 32 位
long int32 位
long long int64 位

各种整数类型之间的主要区别在于它们的大小不同——更大的整数可以容纳更大的数字。

提醒

C++ 只保证整数具有一定的最小大小,而不是特定的确切大小。有关如何在您的机器上确定每种类型的大小,请参阅课程 4.3 -- 对象大小和 sizeof 运算符

题外话…

严格来说,boolchar 类型被认为是整型(因为这些类型将其值存储为整数值)。在接下来的几课中,我们将把这些类型排除在讨论之外。

有符号整数

在日常生活中书写负数时,我们使用负号。例如,-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 intlong intlong 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 的结果是什么?

显示答案

guest
您的电子邮箱地址将不会被显示
发现错误?请在上方留言!
与勘误相关的评论在处理后将被删除,以帮助减少混乱。感谢您帮助使网站对每个人都更好!
来自 https://gravatar.com/ 的头像与您提供的电子邮箱地址相关联。
有回复时通知我:  
407 评论
最新
最早 最多投票
内联反馈
查看所有评论