C++ 11 标准中,基于整数大小的考虑,共提供了如下表所示的这些数据类型。与此同时,标准中还明确限定了各个数据类型最少占用的位数。
整数类型 | 等价类型 | C++11标准规定占用最少位数 |
---|---|---|
short | short int(有符号短整型) | 至少 16 位(2 个字节) |
signed short | short int(有符号短整型) | 至少 16 位(2 个字节) |
signed short int | short int(有符号短整型) | 至少 16 位(2 个字节) |
unsigned short | unsigned short int(无符号短整型) | 至少 16 位(2 个字节) |
unsigned short int | unsigned short int(无符号短整型) | 至少 16 位(2 个字节) |
int | int(有符号整形) | 至少 16 位(2 个字节) |
signed | int(有符号整形) | 至少 16 位(2 个字节) |
signed int | int(有符号整形) | 至少 16 位(2 个字节) |
unsigned | unsigned int(无符号整形) | 至少 16 位(2 个字节) |
unsigned int | unsigned int(无符号整形) | 至少 16 位(2 个字节) |
long | long int(有符号长整形) | 至少 32 位(4 个字节) |
long int | long int(有符号长整形) | 至少 32 位(4 个字节) |
signed long | long int(有符号长整形) | 至少 32 位(4 个字节) |
signed long int | long int(有符号长整形) | 至少 32 位(4 个字节) |
unsigned long | unsigned long int(无符号长整形) | 至少 32 位(4 个字节) |
unsigned long int | unsigned long int(无符号长整形) | 至少 32 位(4 个字节) |
long long(C++11) | long long int(有符号超长整形) | 至少 64 位(8 个字节) |
long long int(C++11) | long long int(有符号超长整形) | 至少 64 位(8 个字节) |
signed long long(C++11) | long long int(有符号超长整形) | 至少 64 位(8 个字节) |
signed long long int(C++11) | long long int(有符号超长整形) | 至少 64 位(8 个字节) |
unsigned long long(C++11) | unsigned long long int(无符号超长整型) | 至少 64 位(8 个字节) |
unsigned long long int(C++11) | unsigned long long int(无符号超长整型) | 至少 64 位(8 个字节) |
C++11 标准规定,每种整数类型必须同时具备有符号(signed)和无符号(unsigned)两种类型,且每种具体的有符号整形和无符号整形所占用的存储空间(也就是位数)必须相同。不过需要注意的是,C++11 标准中只限定了每种类型最少占用多少存储空间,不同的平台可以占用不同的存储空间。
在上表罗列的这些数据类型中,long long 超长整型是 C++ 11 标准新添加的。其实早在 1995 年,就有人提议将 long long 整形写入 C++ 98 标准,但被委员会拒绝了。而后 long long 整形被 C99 标准(C语言标准之一)采纳,并逐渐被很多编译器支持,于是 C++ 标准委员会重新决定将 long long 整形写入 C++ 11 标准中。
如同 long 类型整数需明确标注 “L” 或者 “l” 后缀一样,要使用 long long 类型的整数,也必须标注对应的后缀:
如果不添加任何标识,则所有的整数都会默认为 int 类型。
对于 long long 类型来说,如果想了解当前平台上 long long 整形的取值范围,可以使用
头文件中与 long long 整形相关的 3 个宏,分别为 LLONG_MIN、LLONG_MAX 和 ULLONG_MIN:
1)LLONG_MIN:代表当前平台上最小的 long long 类型整数;
2)LLONG_MAX:代表当前平台上最大的 long long 类型整数;
3)ULLONG_MIN:代表当前平台上最大的 unsigned long long 类型整数(无符号超长整型的最小值为 0)。
举个例子:
#include
#include
#include
using namespace std;
int main()
{
cout <<"long long最大值:" << LLONG_MIN <<" "<< hex << LLONG_MIN <<"\n";
cout << dec <<"long long最小值:" << LLONG_MAX << " " << hex << LLONG_MAX << "\n";
cout << dec << "unsigned long long最大值:" << ULLONG_MAX << " " << hex << ULLONG_MAX;
return 0;
}
程序执行结果为(不唯一):
long long最大值:-9223372036854775808 8000000000000000
long long最小值:9223372036854775807 7fffffffffffffff
unsigned long long最大值:18446744073709551615 ffffffffffffffff
此程序中,输出了各最大值和最小值对应的十六进制,显然在当前平台(Windows10 64位操作系统)上,long long 超长整型占用 64 位(也就是 16 个字节)的存储空间。
实际开发中,避免产生“野指针”最有效的方法,就是在定义指针的同时完成初始化操作,即便该指针的指向尚未明确,也要将其初始化为空指针。
所谓“野指针”,又称“悬挂指针”,指的是没有明确指向的指针。野指针往往指向的是那些不可用的内存区域,这就意味着像操作普通指针那样使用野指针(例如 &p),极可能导致程序发生异常。
C++98/03 标准中,将一个指针初始化为空指针的方式有 2 种:
int *p = 0;
int *p = NULL; //推荐使用
可以看到,我们可以将指针明确指向 0(0x0000 0000)这个内存空间。一方面,明确指针的指向可以避免其成为野指针;另一方面,大多数操作系统都不允许用户对地址为 0 的内存空间执行写操作,若用户在程序中尝试修改其内容,则程序运行会直接报错。
相比第一种方式,我们更习惯将指针初始化为 NULL。值得一提的是,NULL 并不是 C++ 的关键字,它是 C++ 为我们事先定义好的一个宏,并且它的值往往就是字面量 0(#define NULL 0)。
C++ 中将 NULL 定义为字面常量 0,虽然能满足大部分场景的需要,但个别情况下,它会导致程序的运行和我们的预期不符。例如:
#include
using namespace std;
void isnull(void *c){
cout << "void*c" << endl;
}
void isnull(int n){
cout << "int n" << endl;
}
int main() {
isnull(0);
isnull(NULL);
return 0;
}
程序执行结果为:
int n
int n
对于 isnull(0) 来说,显然它真正调用的是参数为整形的 isnull() 函数;而对于 isnull(NULL),我们期望它实际调用的是参数为 void*c 的 isnull() 函数,但观察程序的执行结果不难看出,并不符合我们的预期。
C++ 98/03 标准中,如果我们想令 isnull(NULL) 实际调用的是 isnull(void* c),就需要对 NULL(或者 0)进行强制类型转换:
isnull( (void*)NULL );
isnull( (void*)0 );
如此,才会成功调用我们预期的函数。
由于 C++ 98 标准使用期间,NULL 已经得到了广泛的应用,出于兼容性的考虑,C++11 标准并没有对 NULL 的宏定义做任何修改。为了修正 C++ 存在的这一 BUG,C++ 标准委员会最终决定另其炉灶,在 C++11 标准中引入一个新关键字,即 nullptr。
在使用 nullptr 之前,需保证自己使用的编译器支持该关键字。以 Visual Studio 和 codeblocks 为例,前者早在 2010 版本就对 C++ 11 标准中的部分特性提供了支持,其中就包括 nullptr;如果使用后者,则需将其 G++ 编译器版本至少升级至 4.6.1(同时开启 -std=c++0x 编译选项)。
nullptr 是 nullptr_t 类型的右值常量,专用于初始化空类型指针。nullptr_t 是 C++11 新增加的数据类型,可称为“指针空值类型”。也就是说,nullpter 仅是该类型的一个实例对象(已经定义好,可以直接使用),如果需要我们完全定义出多个同 nullptr 完全一样的实例对象。
值得一提的是,nullptr 可以被隐式转换成任意的指针类型。举个例子:
int * a1 = nullptr;
char * a2 = nullptr;
double * a3 = nullptr;
显然,不同类型的指针变量都可以使用 nullptr 来初始化,编译器分别将 nullptr 隐式转换成 int*、char* 以及 double* 指针类型。
另外,通过将指针初始化为 nullptr,可以很好地解决 NULL 遗留的问题,比如:
#include
using namespace std;
void isnull(void *c){
cout << "void*c" << endl;
}
void isnull(int n){
cout << "int n" << endl;
}
int main() {
isnull(NULL);
isnull(nullptr);
return 0;
}
程序执行结果为:
int n
void*c
借助执行结果不难看出,由于 nullptr 无法隐式转换为整形,而可以隐式匹配指针类型,因此执行结果和我们的预期相符。
总之在 C++11 标准下,相比 NULL 和 0,使用 nullptr 初始化空指针可以令我们编写的程序更加健壮。