4-4 带符号整数

整数integer是一种整数的类型integral type,能够表示正负整数,包括0(例如-2、-1、0、1、2)。C++提供四种主要的基础整数类型供使用:

Type Minimum Size Note
short int 16 bits
int 16 bits Typically 32 bits on modern architectures
long int 32 bits
long long int 64 bits

各种整数类型之间的关键区别在于它们具有不同的大小——更大的整数类型能够容纳更大的数值。

提醒
C++ 仅保证整数具有特定的最小尺寸,而非固定尺寸。有关如何确定每种类型在您机器上的具体大小,请参阅第 4.3 课——对象尺寸与 sizeof 运算符。

顺带一提……
严格来说,bool 和 char 类型被视为整数的类型integral types(因为这些类型将值存储为整数值)。在接下来的几节课中,我们将不讨论这些类型。


有符号整数

在日常书写负数时,我们使用负号。例如,-3表示“负3”。通常我们也会将+3理解为“正3”(尽管惯例要求省略正号前缀)。

这种正数、负数或零的属性称为数的符号sign

C++中的整数默认带符号signed,即数值的符号作为数值本身的一部分存储。因此有符号整数可同时存储正数、负数及零。

本节课我们将重点探讨有符号整数,无符号整数(仅能存储非负数)将在下节课讲解。


有符号整数的定义

以下是定义四种有符号整数类型的推荐方式:

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 short ss;
signed int si;
signed long sl;
signed long long sll;

然而,该关键字不应使用,因为它属于冗余——整数默认即为带符号类型。

最佳实践
优先使用不带 int 后缀或带符号前缀的简写类型。


有符号整数的取值范围

正如上一节所述,n位变量可存储2^n种可能值。但具体哪些值?我们称数据类型能存储的特定值集合为其取值范围range。整型变量的取值范围由两方面决定:其位数(以位为单位)以及是否带符号。

例如,8位有符号整数的取值范围为-128至127。这意味着8位有符号整数可安全存储-128至127(含)之间的任意整数值。

下表列出了不同位宽有符号整数的取值范围:

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

对于擅长数学的人来说,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标准对此给出概括性说明:“若表达式求值结果在数学上未定义或超出其类型可表示值范围,则行为未定义”。通俗而言,这称为溢出overflow

因此,将值140赋给8位有符号整数将导致行为未定义。

当算术运算(如加法或乘法)试图生成超出表示范围的数值时,称为整数溢出integer overflow(或算术溢出arithmetic overflow)。对于有符号整数,整数溢出将导致行为未定义。

#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;
}

在作者的机器上,上述内容打印为:

image

然而,由于第二个输出属于未定义行为的结果,其数值在您的机器上可能存在差异。

对于进阶读者
我们在第4.5课---无符号整数 中探讨了无符号整数溢出时的表现及其规避原因。

通常情况下,溢出会导致信息丢失,这几乎总是不可取的。若怀疑对象可能需要存储超出其范围的值,请使用更大范围的类型!


整数除法

当两个整数相除时,C++ 的行为符合预期——商为整数时:

#include <iostream>

int main()
{
    std::cout << 20 / 4 << '\n';
    return 0;
}

这产生了预期的结果:

image

但让我们看看当整数除法产生小数结果时会发生什么:

#include <iostream>

int main()
{
    std::cout << 8 / 5 << '\n';
    return 0;
}

这可能产生一个出乎意料的结果:

image

在进行两个整数的除法运算(称为整数除法)时,C++ 始终返回整数结果。由于整数无法存储小数部分,任何小数部分都会被直接舍弃(而非四舍五入!)。

仔细观察上述示例:8 / 5 的结果为 1.6。小数部分(0.6)被舍弃,最终结果为 1。换言之,8 / 5 等于 1 余 3,其中余数被舍弃后得到 1。

同理,-8 / 5 的结果为 -1。

警告
使用整数除法时需谨慎,商的任何小数部分都将丢失。但若此为预期结果,整数除法是安全的,因其结果具有可预测性。

若需保留小数结果,我们将在第6.2节——算术运算符中展示实现方法。


测验时间

问题 #1

5-bit有符号整数的取值范围是多少?

显示解答

一个5位数可以表示2⁵=32个不同值。对于带符号整数,这些值几乎均等分配给正数和负数,其中负数额外获得一个值。因此其取值范围为-16到15。

另一种理解方式:5-bit中有1位用于表示符号位,剩余4位表示绝对值。4位可表示16个独立数值:在负数范围内,这4位表示-1至-16;在非负数范围内,则表示0至15。非负数范围少1个数值,因为它包含0。

问题 #2

a) 13 除以 5 的结果是多少?

显示解答

13 ÷ 5 = 2 余 3。余数舍去,得出商为 2。

b) -13 除以 5 的结果是多少?

显示解答

-13 ÷ 5 = -2 余数 -3。余数舍去,结果为 -2。
posted @ 2026-02-13 14:32  游翔  阅读(0)  评论(0)    收藏  举报