Windows 上开发C++程序,使用 long 数据类型发现很容易溢出,才想到 Windows 平台上 long 的字长和 int 一样是 4,而不是 Linux 上常见的 8。

数据类型和字长

根据 C++标准,有:  sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long),同时又规定:

    • char:至少8位(一个字节)
    • short:至少16位(两个字节)
    • int:至少16位(两个字节)
    • long:至少32位(4个字节)
    • long long:至少64位(8个字节)

int 的字长可以是 2,long 的字长可以是 4,因此 MSVC 下 long 和 int 大小一样没问题,只是比较少见。

展开来说,蒸熟类型的字长和编译器及操作系统采用的数据模型有关,已知的数据模型和字长关系有:

数据模型解释short字长int字长long字长long long字长
LP32long和pointer是32位,即2/2/4模型2248
ILP32int、long和pointer是32位,即2/4/4模型2448
LP64long和pointer是64位,即4/8/8模型2488
LLP64long long和pointer是64位,即4/4/8模型2448
ILP64int、long和pointer是64位,即8/8/8模型2888
SILP64short、int、long和pointer是64位8888

 

char 都是8位,即一个字长(byte),目前没见到哪个神经病系统或者编译器使用其他字长

如今的桌面和移动主流处理器基本上支持64地址,因此现在32位模型较少使用(旧硬件、旧系统以及X32 ABI会用到);此外 SILP64 基本上也没见哪个编译器使用,实在是没必要。Windows 上的 MSVC(以及mingw环境下的GCC/clang) 使用的是 LLP64 模型,所以 intlong 都是4个字节。除了Windows,主流操作系统和编译器(绝大部分Linux/Unix、Macos,以及cygwin上的gcc/clang)都采用的是 LP64 模型,这也是为什么一般都会默认long 是8个字节,即64位。

除了 LP64 ,另一个在科学计算领域常见的模型是 ILP64,例如Intel MKL的某些库分 LP64ILP64 版本。在大量数据处理的场景,int32 很容易溢出,因此矩阵/向量行列索引也需要用64位整数。在现代的处理器上,64位整数和32位整数运算的性能基本一致,但是会多占用一倍的内存。如果用到了第三方库(例如OpenBLAS),要让要求使用一致的数据模型,否则可能导致错误的结果。

定长整数类型

鉴于不同数据模型导致的基本类型字长不一样,对于跨平台的保证64位整数,long 是不可靠的。幸运的是,C++11引入了定长整数类型对于定长整数有要求的场景,建议用以下数据类型

    • std::int8/std::uint8
    • std::int16/std::uint16
    • std::int32/std::uint32
    • std::int64/std::uint64

另外一个常用类型时 size_t,一般是 unsigned long long int 的别名,绝大多数情况下都能保证64位,不考虑符号的情况下够用了。但是对于 OpenMP for循环的场景,其要求索引是有符号整数,类似的少数场景下 size_t 无法使用。

对于范围更大的整数需求,例如 int128,C/C++标准未提及,但是不少编译器已经内置支持。如果编译器不支持,则需要手动引入相应的开源代码。

参考

1. 64-bit computing

2. 64-Bit Programming Models: Why LP64?

3. Int32 VS Int64 performance