整型

整型这个词是我们自造的。它不同于整数类型;整数类型还包括布尔类型和字符类型。

分类

一般地,我们称“有符号整数类型”和“无符号整数类型”叫做整型。 为什么叫它们整型?因为这些类型的变量一般用于存放整数。在C++里,整型又被具体地分为许多种类。

类型类型说明符
基本型int
短整型short
short int
长整型long
long int
扩展长整型long long
long long int

可以看到,整型一般分为基本型、短整型、长整型和扩展长整型这四类;上表中同时给出了它们的类型说明符。你会发现,对于同一个类型可能有多个等价的类型说明符——意思就是任意一个都可以使用,效果完全相同。比如对于扩展长整型,你既可以写:

long long int result{0}; // 定义一个叫做 result 的扩展长整型变量

也可以简单写成:

long long result{0};

那么,这四种类型的区别是什么呢?区别就是不同类型的整型变量在内存空间中所占的“存储单元的多少”(简单说叫做存储空间的大小)是不同的。

类型存储单元的多少
基本型 int不少于 16 位(2 字节)
短整型 short不少于 16 位(2 字节)
长整型 long不少于 32 位(4 字节)
扩展长整型 long long不少于 64 位(8 字节)

其中,字节(Byte)是计算机存储空间的基本单位。(类似长度的基本单位叫做“米”,存储空间的基本单位就是字节。)在目前通用的计算机中,一个字节有 8 位。(位(Bit,又译比特)就是指二进制中的一位,是存储空间的最小单位。)

什么意思呢?就是说比如当我们定义一个 int 型的变量的时候,那么计算机就要在它所拥有的存储空间里头开辟一个不少于 16 位长度的存储空间,用来存储这个数据。 显而易见地,正因为它们所占用的字节数不同,所能存储的数据范围也会不同。因为一个 32 位的二进制数能够表达 2322^{32} 个数,而一个 16 位的二进制数所表达的数据的范围只有 2162^{16} 个。(如果你不熟悉这件事情,你可以去问问你的数学老师。这里我不再解释。)

一般地,你要根据自己的需要去决定你要定义整数的类型。比方说你要定义的整数是一个非常小的数——这个数最大都不会超过几千,那你可以把它定义成 short 型,一定程度上节省空间。

值得注意的一点是,C++ 的标准里,关于某种特定的数据类型到底应该占用多大的内存空间,定义得比较宽泛。正如你们所见,C++ 最新的标准只规定了它们至少占用多大空间,却没有具体地定义它们的大小。

但是就目前通用的操作系统来说,整型的大小是这样规定的:

类型> 16 位 Windows32 位 UNIX64 位 UNIX
int4 字节4 字节4 字节
short2 字节2 字节2 字节
long4 字节4 字节8 字节
long long8 字节8 字节8 字节

※ UNIX 指 UNIX 系统(含 macOS, FreeBSD)和类 UNIX 系统(GNU/Linux 等)

除了数据长度的分类,正如之前所说,整型也分为无符号整数类型有符号整数类型。也就是:

类型有符号无符号
基本型int
signed
signed int
unsigned int
unsigned
短整型short
short int
signed short
signed short int
unsigned short
unsigned short int
长整型long
long int
signed long
signed long int
unsigned long
unsigned long int
扩展长整型long long
long long int
signed long long
signed long long int
unsigned long long
unsigned long long int

一个整型数据有无符号这个特性把刚才 4 种基本的子类型再分成不同的两类。比方说 int 类型可以再划分成 signed int 型和 unsigned int 型。那么这里“有无符号”的意思是什么?就是数学上的“正负号”是否存在。换句话说,有符号类型可以存正数,0和负数;而无符号类型只能存储非负数

你也从上面的表格中可以观察到,signed这个词是可以省略的。所以当你写下:

int num{0};

时,就表明你定义了一个有符号基本型整型

signed int num{0};

那么到此为止我们就把那么关于整数数据类型的所有的子类型都介绍完毕了。尽管听上去非常复杂,但其实在大部分情况下,使用 int 型就足够了

存储细节

无符号整数类型

无符号类型整型的存储较为简单。将所存储数的二进制形式逐位存储到内存中即可。例如:

uint

其中 123 的二进制形式为 (1111011)2(1111011)_2 。 因此,32 位的 unsigned int 型能存储的最小值为 00 ,最大值为 23212^{32}-1 ,即 42949672954294967295 。对于其它大小的类型也是这样的道理。

(x)2(x)_2 表示 xx 现在的写法是一个二进制写法。有的书上也会用 x(2)x_{(2)} 来表示二进制数。

有符号整数类型

首先,很自然地,有符号类型整型的存储分为“非负数”和“负数”两个部分,每个部分各占一半。举例来讲,32 位的 signed int 型变量一共能存储 232=42949672962^{32}=4294967296 种数值:其中非负数有 231=21474836482^{31}=2147483648 个;同样地,负数也有 231=21474836482^{31}=2147483648 个。那么它们具体的存储方式是这样的:

二进制存储实际表示的数二进制存储实际表示的数
0000...0000001000...0000231=2147483648-2^{31}=-2147483648
0000...0001111000...0001231+1=2147483647-2^{31}+1=-2147483647
0000...0010221000...0010231+2=2147483646-2^{31}+2=-2147483646
0000...0011331000...0011231+3=2147483645-2^{31}+3=-2147483645
0000...0100441000...0100231+4=2147483644-2^{31}+4=-2147483644
0000...0101551000...0101231+5=2147483643-2^{31}+5=-2147483643
0000...0110661000...0110231+6=2147483642-2^{31}+6=-2147483642
............................
0111...10112315=21474836432^{31}-5=21474836431111...10115-5
0111...11002314=21474836442^{31}-4=21474836441111...11004-4
0111...11012313=21474836452^{31}-3=21474836451111...11013-3
0111...11102312=21474836462^{31}-2=21474836461111...11102-2
0111...11112311=21474836472^{31}-1=21474836471111...11111-1

可以看到,从 0000....00000111....1111 这些首位为 0 的数都是非负数,而从 1000....00001111....1111 这些首位为 1 的数都是负数。所以,有符号类型的存储空间首位也被称作符号位,它指明了一个数是否为负数。

另外还可以注意到,不论符号正负,两部分的数字排列都是从小到大。比如非负数部分从最小的 00 一直排到最大的 23112^{31}-1,而负数部分从最小的 231-2^{31} 一直排列到最大的 1-1 。这样的排列有助于计算机进行基本的算术运算。这种存储方式被称作补码(Two's complement)存储。之所以叫做补码,是因为任何一个数与它的相反数的补码形式恰好“互补”。一个数和它所对应的补码可以根据这个公式进行计算:

二进制数 x 的补码={x,x0取反(x)+1,x<0 \text{二进制数 }x\text{ 的补码}=\begin{cases}x&,x\geqslant0\\\text{取反}(|x|)+1&,x<0\end{cases}

举例来说,我们有

int i{-123};

首先 123-123 的绝对值 123123 的二进制形式为 (1111011)2(1111011)_2 ,我们称其为原码。然后对于 32 位的长度进行取反:取反(NOT)的含义就是对应位反转, 0 变为 11 变为 0 。故取反的结果为:(11111111111111111111111110000100)2(11111111111111111111111110000100)_2 ,称其为反码(Ones' complement)。最后再加1,便得到了补码 (11111111111111111111111110000101)2(11111111111111111111111110000101)_2 。这就是有符号整数类型在计算机中的存储方式。

总结

整型按存储空间的大小分为 4 类;按有无符号分为 2 类,一共 8 小类。每一类的区别主要体现在所能表示的数的范围上。对于大多数的情况,下表整理了它们能表示数的范围:※

类型说明符类型字节数signed 表示范围unsigned 表示范围
int标准型4231-2^{31} ~ 23112^{31}-100 ~ 23212^{32}-1
short短整型2215-2^{15} ~ 21512^{15}-100 ~ 21612^{16}-1
long长整型4231-2^{31} ~ 23112^{31}-100 ~ 23212^{32}-1
long long扩展长整型8263-2^{63} ~ 26312^{63}-100 ~ 26412^{64}-1

※ 在 64 位的 UNIX 或类 UNIX 系统下, long 占用 8 字节,表示范围与 long long 相同。

尽管如此复杂,但在一般情况下 int 足以应付大部分场景。

练习

  1. 尝试编写程序:输入 0 到 5 之间的整数 x,计算 3446744073709550592 的 x 倍。注意使用正确的类型变量来存储。

练习参考答案

#include <iostream>
using namespace std;
int main() {
    unsigned long long a{3446744073709550592};
    int b{0};
    cin >> b;
    a = a * b;
    cout << a << endl;
}
最近更新:
代码未运行