Skip to content

4.4 - 有符号整型

Key Takeaway
  • n 位有符号整型的范围为 \(-(2^{n-1})\)\(2^{n-1}-1\)
  • 有符号整型的溢出会导致未定义行为

整型是一种整数类型,它可以表示正整数和负整数,当然也包括 0(例如:-2、-1、0、1、2)。C++ 中有 4 中基础整型:

Type Minimum Size Note
short 16 bits
int 16 bits 现代计算机体系结构下通常为 32 位
long 32 bits
long long 64 bits

这些类型的主要差异是它们的大小不同——越大的整型可以表示越大的数。

提醒

C++ 只保证整型占用空间的最小值,而不保证其具体值。请参考4.3 - 对象的大小和 sizeof 操作符来确定每种类型在你机器上的具体大小。

有符号整型

在日常生活中,如果我们要表示负整数,通常会使用负号。例如,-3 表示 “负三”。我们还会使用+3 来表示”正三“(尽管通常正号会被省略)。数字的这种“正负零”属性,称为数字的负号。

默认情况下,整型是有符号的,也就是说数字的符号是数字本身的一部分(使用一个位进行存储,称为符号位)。因此,有符号整型可以表示正数负数和 0。

在这节课中,我们会专注于介绍有符号整型(signed)。无符号(unsigned)整型(只能保存非负值)则会在后续的课程中进行讨论。

相关内容

我们在 O.4 -- Converting between binary and decimal 中讨论了二进制表示法时符号位如何使用。

定义有符号整型

定义有符号整型时,推荐的方式如下:

1
2
3
4
short s;
int i;
long l;
long long ll;

所有的整型(除了 int)以外,都可以带上一个可选的 int 后缀:

1
2
3
short int si;//short int 中的 int 即可选后缀
long int li;
long long int lli;

不过,并不推荐使用这些后缀。使用这些后缀不仅需要打更多字,而且添加 int 后缀后,不容易将这些类型和 int 类型的变量区分开来,而且如果不经意忘记了 short 或者 long 修饰符的话则可能会引入问题。

整型还可以添加一个可选的 singed 关键字,通常会将其放置在类型名前:

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

不过,这个关键字也不推荐使用,因为它是多余的,整型默认就是有符号的。

最佳实践

使用最精简的写法,不要使用 int 后缀或 signed 前缀。

有符号整型的范围

正如之前介绍的那样,在内存中占用 n 位的变量,可以保存 \(2^n\) 个可能的值。但是,具体是哪些值呢?我们称一种类型可以表示的值为其范围。而整型的范围有两个因素决定:大小(多少位),是否有符号。

根据定义,8 位有符号整型可以表示的范围是:-128 到 127。也就是说一个有符号整型可以安全地表示 -128 到 127 (包括) 中的任意整数。

题外话

数学时间:8 位整型有 8 个位,2 的 8 次方是 256,所以它一共可以保存 256 个可能的值。在 -128 到 127 中一共有 256 个可能的整数值。

下表展示了不同有符号整型可以表示的范围:

大小/类型 范围
8 bit signed -128 到 127
16 bit signed -32,768 到 32,767
32 bit signed -2,147,483,648 到 2,147,483,647
64 bit signed -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807

对于喜欢数学表示法的人来说: n 位有符号整型的范围为 \(-(2^{n-1})\)\(2^{n-1}-1\)

对于不喜欢数学的人来说,参考上表就可以了。

整数溢出

如果我们尝试把 280 赋值给一个 8 位有符号整型会发生什么呢?这个数字已经超出了 8 位有符号整型数能够表示的范围。280 需要 9 个位(加一个符号位)才能表示,但是一个8位整型只有7个位(加一个符号位)。

当需要存储的值的范围超过了变量可以表示的范围,就会发生整数溢出(通常简称为溢出) 。这是因为我们需要表示的数,需要比该变量能够使用的更多的位。这种情况下,变量将没有足够的内存空间可以用来存放数据。

对于有符号整型来说,究竟哪个位的数据会被丢弃并没有明确的定义,因此有符号整型的溢出是未定义行为。

注意

有符号整型的溢出会导致未定义行为

通常来说,溢出会导致信息的丢失,这是我们最不希望发生的是。如果你怀疑对象需要保存的值有可能会超过它能够表示的范围,请使用范围更大的类型!

整型除法

当对两个整型进行除法运算时, 如果商是整数,则结果应该如你所料:

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

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

计算结果为 5。

但是,让我们看看这个商是小数的例子:

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

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

计算结果为 1。

当 C++对两个整型做除法时(称为整数除法),它只能产生整数结果,因为整型不能保存小数值,结果中的小数部分会被丢弃(而不是四舍五入)。

仔细看看上面的例子,8 / 5 应该等于1.6,但是小数部分(0.6)会被丢弃,所以结果为1。

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

注意

在使用整数除法时要小心,因为商中的小数部分会被丢弃。不过,如果这恰好是你期望的,那么可以放心使用。

如果你希望保留小数部分,请参考 5.2 - 数学运算符 中介绍的方法。