首页 > 编程笔记

C++ long long整形用法详解

整型 long long 虽然是 C++11 才新加入标准的,但是我们似乎很早就开始使用这个类型了,这其中包含了一个有趣的故事。

long long 这个类型早在 1995 年 6 月之前就由罗兰·哈廷格(Roland Hartinger)提出申请加入 C++ 标准。但是当时的 C++ 标准委员会以 C 语言中不存在这个基本类型为由,拒绝将这个类型加入 C++ 中。

而就在 C++98 标准出台的一年后,C99 标准就添加了 long long 这个类型,并且流行的编译器也纷纷支持了该类型,这也就是我们很早就接触到 long long 的原因。在此之后 C++ 标准委员会在 C++11 中才有计划将整型 long long 加入标准中。

我们知道 long 通常表示一个 32 位整型,而 long long 则是用来表示一个 64 位的整型。不得不说,这种命名方式简单粗暴。不仅写法冗余,而且表达的含义也并不清晰。如果按照这个命名规则,那么128位整型就该被命名为long long long了。但是不管怎么样,long long 既然已经加入了 C++11 的标准,那么我们能做的就是适应它,并且希望不会有 long long long 这种类型的诞生。

C++ 标准中定义,long long 是一个至少为 64 位的整数类型。请注意这里的用词“至少”,也就说 long long 的实际长度可能大于 64位,不过我至今也没有看到大于 64 位长度的 long long 出现。

另外,long long 是一个有符号类型,对应的无符号类型为 unsigned long long,当然读者可能看到过诸如 long long int、unsigned long long int 等类型,实际上它们和 long long、unsigned long long 具有相同的含义。

C++ 标准还为其定义 LL 和 ULL 作为这两种类型的字面量后缀,所以在初始化 long long 类型变量的时候可以这么写:
long long x = 65536LL;
当然,这里可以忽略 LL 这个字面量后缀,直接写成下面的形式也可以达到同样的效果:
long long x = 65536;
要强调的是,字面量后缀并不是没有意义的,在某些场合下我们必须用到它才能让代码的逻辑正确,比如下面的代码:
long long x1 = 65536 << 16; // 计算得到的x1值为0、
std::cout << "x1 = " << x1 << std::endl;
long long x2 = 65536LL << 16; // 计算得到的x2值为4294967296(0x100000000)
std::cout << "x2 = " << x2 << std::endl;
以上代码的目的是将 65536 左移 16 位,以获得一个更大的数值。但是,x1 计算出来的值却是 0,没有增大反而减小了。原因是在没有字面量后缀的情况下,这里的 65536 被当作 32 位整型操作,在左移 16 位以后,这个 32 位整型的值变成了 0,所以事实是将 0 赋值给了 x1,于是我们看到 x1 输出的结果为 0。

而在计算 x2 的过程中,代码给 65536 添加了字面量后缀 LL,这使编译器将其编译为一个 64 位整型,左移 16 位后仍然可以获得正确的结果:4294967296(0x100000000)。另外,有些编译器可能在编译 long long x1 = 65536 << 16; 的时候显示一些警告提示,而另一些编译器可能没有,无论如何我们必须在编写代码的时候足够小心,避免上面情况的发生。

和其他整型一样,long long 也能运用于枚举类型和位域,例如:
enum longlong_enum : long long {
    x1,
    x2
};
struct longlong_struct {
    long long x1 : 8;
    long long x2 : 24;
    long long x3 : 32;
};
std::cout << sizeof(longlong_enum::x1) << std::endl; // 输出大小为8
std::cout << sizeof(longlong_struct) << std::endl; // 输出大小为8
作为一个新的整型 long long,C++ 标准必须为它配套地加入整型的大小限制。在头文件中增加了以下宏,分别代表 long long 的最大值和最小值以及 unsigned long long 的最大值:
#define LLONG_MAX 9223372036854775807LL         // long long的 大值
#define LLONG_MIN (-9223372036854775807LL - 1)  // long long的 小值
#define ULLONG_MAX 0xffffffffffffffffULL        // unsigned long long的 大值
在 C++ 中应该尽量少使用宏,用模板取而代之是明智的选择。C++ 标准中对标准库头文件做了扩展,特化了 long long 和 unsigned long long 版本的 numeric_ limits 类模板。这使我们能够更便捷地获取这些类型的最大值和最小值,如下面的代码示例:
#include <iostream>
#include <limits>
#include <cstdio>
using namespace std;

int main(int argc, char* argv[])
{
    // 使用宏方法
    cout << "LLONG_MAX = " << LLONG_MAX << endl;
    cout << "LLONG_MIN = " << LLONG_MIN << endl;
    cout << "ULLONG_MAX = " << ULLONG_MAX << endl;
    // 使用类模板方法
    cout << "std::numeric_limits<long long>::max() = " << numeric_limits<long long>::max() << endl;
    cout << "std::numeric_limits<long long>::min() = " << numeric_limits<long long>::min() << endl;
    cout << "std::numeric_limits<unsigned long long>::max() = " << numeric_limits<unsigned long long>::max() << endl;

    // 使用printf打印输出     
    printf("LLONG_MAX = %lld\n", LLONG_MAX);
    printf("LLONG_MIN = %lld\n", LLONG_MIN);
    printf("ULLONG_MAX = %llu\n", ULLONG_MAX);
    return 0;
}
运行结果为:

LLONG_MAX = 9223372036854775807
LLONG_MIN = -9223372036854775808
ULLONG_MAX = 18446744073709551615
std::numeric_limits<long long>::max() = 9223372036854775807
std::numeric_limits<long long>::min() = -9223372036854775808
std::numeric_limits<unsigned long long>::max() = 18446744073709551615
LLONG_MAX = 9223372036854775807
LLONG_MIN = -9223372036854775808
ULLONG_MAX = 18446744073709551615

以上代码很容易理解,唯一需要说明的一点是,随着整型 long long 的加入,std::printf 也加入了对其格式化打印的能力。新增的长度指示符 ll 可以用来指明变量是一个 long long 类型,所以我们分别使用 %lld 和 %llu 来格式化有符号和无符号的 long long 整型了。当然,使用 C++ 标准的流输入/输出是一个更好的选择。

推荐阅读