最安全的指针初始化方式是直接用 nullptr,因其类型安全、避免重载歧义;原生指针需显式初始化,成员指针应在构造函数初始化列表中设为 nullptr,静态成员需类外定义初始化,智能指针默认为空无需显式赋值。

直接用 = nullptr 初始化最安全
在 C++11 及以后,nullptr 是专用于指针的空值字面量,类型安全、不会隐式转成整数。初始化指针时直接赋值即可,编译器会拒绝把 nullptr 赋给非指针类型,避免老式 NULL 或 0 带来的重载歧义。
常见写法包括:
-
int* p = nullptr;(推荐,清晰且通用) -
int* p{nullptr};(统一初始化语法,同样有效) -
int* p{};(值初始化,对原生指针等价于= nullptr)
注意:不要写 int* p = NULL; —— NULL 在多数实现中是 0 或 0L,本质是整数宏,可能在函数重载中被误选为 int 参数而非指针。
成员指针初始化要放在构造函数初始化列表里
类内指针成员不能在声明处直接用 = nullptr(C++11 允许但不推荐用于非常量静态成员),必须在构造函数初始化列表中显式初始化,否则未定义行为风险高。
错误示范:class A { int* p = nullptr; }; // C++11 允许,但若后续有自定义构造函数,此处会被忽略
正确做法:
- 始终在构造函数初始化列表中初始化:
A() : p(nullptr) {} - 若有多重构造函数,每个都需确保初始化,或使用委托构造函数统一处理
- 静态成员指针需在类外定义并初始化:
int* A::static_p = nullptr;
数组指针和智能指针的初始化差异
原生指针数组(如 int* arr[5])每个元素仍需单独初始化;而 std::unique_ptr 和 std::shared_ptr 默认构造即等价于空状态,无需显式写 nullptr。
-
int* ptrs[3] = {nullptr, nullptr, nullptr};(必须逐个赋) -
std::unique_ptr<int> up;</int>等价于std::unique_ptr<int>(nullptr)</int> -
std::shared_ptr<int> sp = nullptr;</int>合法,但更惯用std::shared_ptr<int>()</int>
混用时尤其注意:把原生指针传给智能指针构造函数后,原生指针不会自动变 nullptr,必须手动置空,否则双重释放。
忘记初始化导致的典型崩溃现象
未初始化的指针(尤其是栈上或类成员)值是随机的,解引用时大概率触发 Segmentation fault 或 Access violation。调试器常显示地址类似 0x7fffeff8a01c —— 这不是 nullptr,而是野指针。
- Clang/GCC 开启
-Wuninitialized可捕获部分栈变量未初始化问题 - AddressSanitizer(
-fsanitize=address)能检测野指针读写,但无法保证 100% 覆盖 - 成员指针在构造函数体中赋值(而非初始化列表)仍属“延迟初始化”,中间存在空窗期
真正可靠的防御是:声明即初始化,不依赖“后面再设”。哪怕只是临时占位,也先写 = nullptr。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











