C++新基础类型long long, char8_t, char16_t, char32_t

一、整数类型 long long

我们知道long通常表示一个32位整型,而long long则是用来表示一个64位的整型。

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

另外,
long long是一个有符号类型,对应的无符号类型为unsignedlong 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;

要强调的是,字面量后缀并不是没有意义的,在某些场合下我们必须用到它才能让代码的逻辑正确,比如下面的代码:

#include <iostream>

using namespace std;

int main(int, char **)
{
    long long x1 = 65536 << 16;
    cout << "x1=" << x1 << endl;

    long long x2 = 65536LL << 16;
    cout << "x2=" << x2 << 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;的时候显示一些警告提示,而另一些编译器可能没有,无论如何我们必须在编写代码的时候足够小心,避免上面情况的发生。

20230108173807

作为一个新的整型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 longlong的最大值

在C++中应该尽量少使用宏,用模板取而代之是明智的选择。C++标准中对标准库头文件做了扩展,特化了long long和unsigned long long版本的numeric_ limits类模板。这使我们能够更便捷地获取这些类型的最大值和最小值,如下面的代码示例:

#include <iostream>
#include <climits>
#include <limits>
#include <cstdio>

using namespace std;

void func1()
{
    long long x1 = 65536 << 16;
    cout << "x1=" << x1 << endl;

    long long x2 = 65536LL << 16;
    cout << "x2=" << x2 << endl;
}

void func2()
{
    // 使用宏方法
    cout << "LLONG_MAX=" << LLONG_MAX << endl;
    cout << "LLONG_MIN=" << LLONG_MIN << endl;
    cout << "ULLONG_MAX=" << ULONG_LONG_MAX << endl;

    // 使用类模板方法
    cout << "std::numeric_limits<long long>::max()=" << std::numeric_limits<long long>::max() << endl;
    cout << "std::numeric_limits<long long>::min()=" << std::numeric_limits<long long>::min() << endl;
    cout << "std::numeric_limits<unsigned long long>::max=" << std::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);
}

int main(int, char **)
{
    // func1();
    func2();
}

输出结果如下:

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++标准的流输入/输出是一个更好的选择。

二、新字符类型 char16_t 和 char32_t

Unicode字符集有UTF-8、UTF-16和UTF-32这3种编码方法。

对于UTF-8编码方法而言,普通类型似乎是无法满足需求的,毕竟普通类型无法表达变长的内存空间。所以一般情况下我们直接使用基本类型char进行处理,而过去也没有一个针对UTF-16和UTF-32的字符类型。到了C++11,char16_t和char32_t的出现打破了这个尴尬的局面。在C++11标准中添加两种新的字符类型char16_t和char32_t,它们分别用来对应Unicode字符集的UTF-16和UTF-32两种编码方法。除此之外,C++11标准还为3种编码提供了新前缀用于声明3种编码字符和字符串的字面量,它们分别是UTF-8的前缀u8、UTF-16的前缀u和UTF-32的前缀U:

char utf8c = u8'a'; // C++17标准
//char utf8c = u8'好';
char16_t utf16c = u'好';
char32_t utf32c = U'好';
char utf8[] = u8"你好世界";
char16_t utf16[] = u"你好世界";
char32_t utf32[] = U"你好世界";

在上面的代码中,分别使用UTF-8、UTF-16和UTF-32编码的字符和字符串对变量进行了初始化,代码很简单,不过还是有两个地方值得一提。

char utf8c = u8’a’在C++11标准中实际上是无法编译成功的,因为在C++11标准中u8只能作为字符串字面量的前缀,而无法作为字符的前缀。这个问题直到C++17标准才得以解决,所以上述代码需要C++17的环境来执行编译。

char utf8c = u8’好’是无法通过编译的,因为存储“好”需要3字节,显然utf8c只能存储1字节,所以会编译失败。

三、char8_t 字符类型

使用char类型来处理UTF-8字符虽然可行,但是也会带来一些困扰,比如当库函数需要同时处理多种字符时必须采用不同的函数名称以区分普通字符和UTF-8字符。C++20标准新引入的类型char8_t可以解决以上问题,它可以代替char作为UTF-8的字符类型。char8_t具有和unsigned char相同的符号属性、存储大小、对齐方式以及整数转换等级。引入char8_t类型后,在C++17环境下可以编译的UTF-8字符相关的代码会出现问题,例如:

char str[] = u8"text"; // C++17编译成功;C++20编译失败,需要char8_t
char c = u8'c';

当然反过来也不行:

char8_t c8a[] = "text"; // C++20编译失败,需要char
char8_t c8 = 'c';

参考文献:《现代C++语言核心特性解析》

猜你喜欢

转载自blog.csdn.net/hubing_hust/article/details/128604346
今日推荐