19-5 空指针

空指针void pointer(也称为泛型指针)是一种特殊的指针类型,它可以指向任何数据类型的对象!空指针的声明方式与普通指针相同,只需将指针类型声明为 void 即可:

void* ptr {}; // ptr is a void pointer

空指针可以指向任何数据类型的对象:

int nValue {};
float fValue {};

struct Something
{
    int n;
    float f;
};

Something sValue {};

void* ptr {};
ptr = &nValue; // valid
ptr = &fValue; // valid
ptr = &sValue; // valid

然而,由于空指针无法识别其所指向对象的类型,直接解引用空指针是非法的。必须先将空指针强制转换为其他指针类型,才能进行解引用操作。

int value{ 5 };
void* voidPtr{ &value };

// std::cout << *voidPtr << '\n'; // illegal: dereference of void pointer

int* intPtr{ static_cast<int*>(voidPtr) }; // however, if we cast our void pointer to an int pointer...

std::cout << *intPtr << '\n'; // then we can dereference the result

这将输出:(上面代码需要放到main函数里面,并包含头文件)

image

接下来显而易见的问题是:既然空指针不知道它指向什么,我们又该如何确定将其转换为何种类型?归根结底,这需要你自行追踪管理。

以下是一个空指针的应用示例:

#include <cassert>
#include <iostream>

enum class Type
{
    tInt, // note: we can't use "int" here because it's a keyword, so we'll use "tInt" instead
    tFloat,
    tCString
};

void printValue(void* ptr, Type type)
{
    switch (type)
    {
    case Type::tInt:
        std::cout << *static_cast<int*>(ptr) << '\n'; // cast to int pointer and perform indirection
        break;
    case Type::tFloat:
        std::cout << *static_cast<float*>(ptr) << '\n'; // cast to float pointer and perform indirection
        break;
    case Type::tCString:
        std::cout << static_cast<char*>(ptr) << '\n'; // cast to char pointer (no indirection)
        // std::cout will treat char* as a C-style string
        // if we were to perform indirection through the result, then we'd just print the single char that ptr is pointing to
        break;
    default:
        std::cerr << "printValue(): invalid type provided\n";
        assert(false && "type not found");
        break;
    }
}

int main()
{
    int nValue{ 5 };
    float fValue{ 7.5f };
    char szValue[]{ "Mollie" };

    printValue(&nValue, Type::tInt);
    printValue(&fValue, Type::tFloat);
    printValue(szValue, Type::tCString);

    return 0;
}

该程序输出:

image


空指针杂谈

空指针可被设置为空值:

void* ptr{ nullptr }; // ptr is a void pointer that is currently a null pointer

由于空指针无法识别其指向的对象类型,删除空指针将导致未定义行为。若需删除空指针,请先使用static_cast将其转换回相应类型。

无法对 void 指针进行指针运算。这是因为指针运算要求指针知道其指向对象的大小,以便正确地递增或递减指针。

请注意,不存在 void 引用。这是因为 void 引用将属于 void & 类型,且无法确定其引用的值类型。


结论

总的来说,除非绝对必要,否则最好避免使用空指针,因为它们实际上允许你绕过类型检查。这会让你无意中做出毫无意义的操作,而编译器不会对此提出警告。例如,以下代码是有效的:

int nValue{ 5 };
printValue(&nValue, Type::tCString);

image

但谁知道实际结果会如何!

虽然上述函数看似能让单个函数处理多种数据类型,但C++其实提供了更优方案(通过函数重载)来实现相同功能,同时保留类型检查以防止误用。许多曾经需要使用空指针处理多种数据类型的场景,如今更适合采用模板实现,后者同样能提供强类型检查。

不过,极少数情况下,空指针仍可能有合理用途。但请务必先确认是否存在更优(更安全)的替代方案!


测验时间

问题 #1

void pointer与null pointer有何区别?

显示解答

`void pointer`是一种可以指向任意类型对象的指针,但它本身并不了解所指向对象的具体类型。要实现间接引用,必须将`void pointer`显式转换为其他类型的指针。`null pointer`则是不指向任何地址的指针。`void pointer`可以是`null pointer`。

因此,`void pointer`描述的是指针的类型,而`null pointer`描述的是指针的值(地址)。
posted @ 2026-01-17 19:04  游翔  阅读(0)  评论(0)    收藏  举报