章节目录

有符号整数

本节阅读量:

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

类型 最小大小(字节)
short int 2
int 2 现代机器上通常是4个字节
long int 4
long long int 8

不同整数类型之间的关键区别在于大小不同——较大的整数类型可以表示更大的数字。


有符号整数

在日常生活中书写负数时,我们使用负号。例如,-3表示”负3”。我们通常也能识别+3为”正3”(尽管按照惯例,正号通常会被省略)。

正、负或零的属性称为数字的符号。

默认情况下,C++中的整数是有符号的,这意味着数字的符号存储为数字的一部分。因此,有符号整数可以同时包含正数和负数(和0)。

在本课中,我们将重点讨论有符号整数。在下一课中,我们将讨论无符号整数(它只能容纳非负数)。


定义有符号整数

下面是定义四种类型的有符号整数的首选方法:

1
2
3
4
short s;      // 优先使用 "short" 而不是 "short int"
int i;
long l;       // 优先使用 "long" 而不是 "long int"
long long ll; // 优先使用 "long long" 而不是 "long long int"

尽管short int、long int或long long int也能正常使用,但我们更推荐使用这些类型的简写形式(不带int后缀)。添加int后缀会使这些类型更难与int类型的变量区分开来。如果不小心遗漏了short或long修饰符,就可能引发错误。

整数类型也可以加上可选的signed关键字,按照惯例放在类型名称之前:

1
2
3
4
signed short ss;
signed int si;
signed long sl;
signed long long sll;

然而,不应该使用该关键字,因为它是多余的——整数默认就是有符号的。


有符号整数范围

正如你在上一节中了解到的,一个n位的变量可以保存2的n次方个可能的值。但具体能表示哪些值呢?整数变量的取值范围由两个因素决定:它的大小(以位为单位)和它是否有符号。

根据定义,8位有符号整数的范围是-128到127。这意味着有符号整数可以安全地存储-128到127(含)之间的任何整数值。

下面是不同大小的有符号整数的取值范围表:

类型大小 取值范围
8位 -128 to 127
16位 -32,768 to 32,767
32位 -2,147,483,648 to 2,147,483,647
64位 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

用数学公式来表示,n位有符号变量的范围为-2^(n-1)到2^(n-1)-1。


溢出(Overflow)

如果尝试将值140赋给8位有符号整数,会发生什么?这个数字超出了8位有符号整数所能容纳的范围。数字140需要9位来表示(8个量值位和1个符号位),但8位有符号整数只有8位可用(7个量值位和1个符号位)。

C++20标准对此有一条总括性声明:”如果在表达式求值过程中,结果在数学上未定义,或者不在其类型的可表示值范围内,则行为是未定义的。”通俗地说,这就是所谓的溢出。

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

如果算术运算(如加法或乘法)产生的结果超出了可表示的范围,就称为整数溢出(或算术溢出)。对于有符号整数,整数溢出会导致未定义行为。

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


整数除法

对两个整数进行除法时,如果结果恰好是整数,C++的运算结果与预期一致:

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

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

这会产生预期的结果:

1
5

但让我们看看当整数除法的结果包含小数部分时会发生什么:

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

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

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

1
1

对两个整数进行除法(即整数除法)时,C++始终产生整数结果。由于整数无法保存小数部分,任何小数部分都会被直接丢弃(注意是截断,不是四舍五入!)。

仔细看上面的例子,8/5的精确值为1.6。丢弃小数部分(0.6)后,结果为1。换一种说法,8/5等于1余3,余数被丢弃,只保留1。

类似地,-8/5产生值-1。

如果需要得到带小数的结果,我们将在后续课程中介绍具体做法。


4.2 对象大小和sizeof运算符

上一节

4.4 无符号整数,以及为什么要避免使用它们

下一节