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函数里面,并包含

接下来显而易见的问题是:既然空指针不知道它指向什么,我们又该如何确定将其转换为何种类型?归根结底,这需要你自行追踪管理。
以下是一个空指针的应用示例:
#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;
}
该程序输出:

空指针杂谈
空指针可被设置为空值:
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);

但谁知道实际结果会如何!
虽然上述函数看似能让单个函数处理多种数据类型,但C++其实提供了更优方案(通过函数重载)来实现相同功能,同时保留类型检查以防止误用。许多曾经需要使用空指针处理多种数据类型的场景,如今更适合采用模板实现,后者同样能提供强类型检查。
不过,极少数情况下,空指针仍可能有合理用途。但请务必先确认是否存在更优(更安全)的替代方案!
测验时间
问题 #1
void pointer与null pointer有何区别?
显示解答
`void pointer`是一种可以指向任意类型对象的指针,但它本身并不了解所指向对象的具体类型。要实现间接引用,必须将`void pointer`显式转换为其他类型的指针。`null pointer`则是不指向任何地址的指针。`void pointer`可以是`null pointer`。
因此,`void pointer`描述的是指针的类型,而`null pointer`描述的是指针的值(地址)。

浙公网安备 33010602011771号