nullptr

2018-08-02  本文已影响0人  ColdWave

nullptr

空指针:从 0 到 NULL,再到 nullptr

NULL 是一个宏定义:

#undef NULL
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
int *my_ptr = 0;
int *my_ptr = NULL;
// NULL 的问题
#include <stdio.h>

void f(char *c) {
    printf("invoke f(char *)\n);
}
void f(int i) {
    printf("invoke f(int)\n");
}
int main() {
    f(0);
    f(NULL);     // 注意:如果gcc编译,NULL会转换为内部标识符 __null,该语句会编译失败
    f((char*)0);
}
/*
 * 使用 XLC 编译器会得到如下结果:
 * invoke f(int)
 * invoke f(int)
 * invoke f(char*)
 * XLC 将 NULL 定义为了 0
*/

引起该问题的原因是 0 的二义性。0 既可以表示整型,也可以表示一个 空指针(void *)。存在二义性时,需要使用强制类型转换:((void *)0).

虽然 g++ 编译器将 NULL 转换为编译器内部标识符(__null),并在编译时期进行了分析,在一定程度上可以缓解问题,但是会带来代码移植的限制。

nullptr

// 头文件: cstddef
typedef decltype(nullptr) nullptr_t;
/*
 使用 nullptr_t 必须包含头文件: cstddef。
 使用 nullptr 则不需要
*/

nullptr 最大的优势是 有类型,且可以被隐式转换为指针类型。

#include <stdio.h>

void f(char *c) {
    printf("invoke f(char *)\n);
}
void f(int i) {
    printf("invoke f(int)\n");
}
int main() {
    f(nullptr); // invoke f(char *)
    f(0);       // invoke f(int)
}

nullptr 和 nullptr_t

C++11 不仅定义了空指针常量 nullptr,也定义了 空指针类型 nullptr_t。那么也就是说 nullptr_t 可以用来定义变量。通常,可以使用 nullptr_t 声明一个 空指针变量。

#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
    // nullptr 可以隐式转换为 char*
    char *cp = nullptr;

    // 不可转换为整型,而其他类型也不能转换为 nullptr_t
    // int n1 = nullptr;
    // int n2 = reinterpret_cast<int>(nullptr);
    // nullptr 与 nullptr_t 类型变量可以比较
    // 当使用 ==, <=, >= 符号比较时返回 true。
    nullptr_t nptr;
    if (nptr == nullptr) {
        cout << "nullptr_t nptr == nullptr" << endl;
    } else {
        cout << "nullptr_t nptr != nullptr" << endl;
    }
    if (nptr < nullptr) {
        cout << "nullptr nptr < nullptr" << endl;
    } else {
        cout << "nullptr_t nptr !< nullptr" << endl; 
    }

    // 不能转换为整型或 bool 类型
    // if (0 == nullptr);
    // if (nullptr);

    // 不可以进行算术运算
    // nullptr += 1;
    // nullptr * 5;

    // 以下操作均可以正常运行
    sizeof(nullptr);
    typeid(nullptr);
    throw(nullptr);

    return 0;
}

虽然 nullptr_t 看起来像是一个 指针类型,但是在把 nullptr_t 应用于模板时,我们发现模板却只能把他作为一个普通的类型来推导。(并不会将其视为 T* 指针)

using namespace std;
template<typename T> void g(T* t) {}
template<typename T> void h(T t) {}

int main()
{
    g(nullptr);          // error,nullptr 时 nullptr_t 类型,而不是指针
    g((float*)nullptr);  // T = float

    h(0);                // T = int
    h(nullptr);          // T = nullptr_t
    h((float*)nullptr);  // T = float *

}

一些关于 nullptr 规则的讨论

#include <cstdio>
#include <cstddef>
using namespace std;
int main()
{
    nullptr_t my_null;
    printf("%x\n", &my_null);
    
    // printf("%x", &nullptr);  // 根据 C++11 的规定,nullptr 是右值常量,无法取地址
    
    printf("%d\n", my_null == nullptr);

    const nullptr_t && default_nullptr = nullptr;
    // default_nullptr 是 nullptr 的一个右值引用
    printf("%x\n", &default_nullptr);
}
上一篇 下一篇

猜你喜欢

热点阅读