当前位置: 首页 > news >正文

在C++中空指针用NULL来初始化还合适吗

问题引入

在C++11的新特性中,我们使用nullptr来表示指针空值。那为什么放着好好的NULL不用未使用nullptr呢?那肯定是发明C++的大佬们发现了什么NULL使用过程的bug,让我们一起来探讨一下。

1. C++98中的空指针

我们知道,良好的编程习惯要求我们,在定义指针时就要进行赋初值(初始化),否则将会产生难以预料的错误。

1.1 指针的危险性

C++在创建指针时,会分配用来储存这个指针本身(也就是用来储存地址)的内存空空间,但是并不会分配指针所指向的内存空间。而为数据提供空间是一个独立的步骤,如果忽略这一步无疑是自找麻烦:

代码语言:javascript

AI代码解释

int* fellow; *fellow = 1234;

我们画图来理解(图中的内存地址是编造的):

代码语言:javascript

AI代码解释

int* fellow;

这一句的作用就是,定义了一个指针follow,并将地址为0x0001的空间分配给了follow。然后我们并没有进行指针的初始化,所以此时0x0001这块内存中将会储存一个随机的地址(我们也不知道会是什么),而上面背景中的意思就是,这个随机储存的地址的空间并不会随之分配。

这里我们假设那个随机的地址是0x0080,然后执行下面:

代码语言:javascript

AI代码解释

*fellow = 1234;

也就是说我们给一个没有初始化的指针(一个随机地址0x0080)上赋值了1234,如果0x0080正好使我们follow需要的地址(但是这个概率相当于大海捞针)还能接受,但是这个地址一旦是某一个程序已经使用的内存空间将会发生不可估计的错误,可能是数据损坏,甚至系统崩溃

因此为了避免这个问题,我们都要给指针进行初始化。通常情况下我们是这样进行初始化的:

代码语言:javascript

AI代码解释

int main() { //空指针定义 int* p1 = NULL; int* p2 = 0; return 0; }

实际上,NULL是一个宏定义,我们在C语言的传统文件中就可以看到如下代码:

可以看到NULL,可能被定义为字面常量0,或者是无类型指针(void*)的常量。但是使用这种空值的指针会出现一下新的问题,来分析一下这段代码:

代码语言:javascript

AI代码解释

void f(int) { cout << "f(int)" << endl; } void f(int*) { cout << "f(int*)" << endl; } int main() { f(0); f(NULL); return 0; }

我们定义两个函数(对这里的函数重载有疑问的可以看),按照正常的想法,应该是f(0)进入f(int),f(NULL)进入f(int*),所以我们预期的打印结果是:

代码语言:javascript

AI代码解释

f(int) f(int*)

但是运行结果:

很明先与我们想的有出入,但是这是为什么呢?

其实应该比较容易想清楚,我们本来想使用空指针NULL这样能够进入f(int*),但是在预处理阶段NULL就已经被宏定义替换成了0,所以也就进入了函数f(int),这也就是为什么打印出两个相同的结果。

在C++98中,NULL既可以是一个整型数字0,也可以是一个没有类型的指针(void*)常量,但是编译器默认会先将其当做一个整型数字0,如果想要编译器将其当做指针来处理,就必须要进行void*。


2. C++11中的空指针

发明C++的大佬们在C++11中,为了避免这个问题,使用关键字nullptr来表示空指针。

代码语言:javascript

AI代码解释

int main() { f(0); f(NULL); f(nullptr); return 0; }

我们加上nullptr来看一下运行结果:

此时我们会发现nullptr就解决了这个问题。

注意:

  • 使用nullptr时,并不需要包含头文件,因为C++11中,nullptr是作为关键字引入的
  • 在C++11中sizeof(nullptr)和sizeof(void*)

  • 为了代码的健壮性,还是希望小伙伴们习惯在初始化空指针的时候使用nullptr

(本篇完)


http://www.jsqmd.com/news/729217/

相关文章:

  • 数据结构面试官最爱问的10个问题,我帮你整理好了(附详细答案)
  • 别再手动调间距了!用Ant Design的labelCol和wrapperCol搞定表单布局(附响应式技巧)
  • 开源监控聚合平台Synmetrix:统一多源指标,构建可观测性语义层
  • Claude会话保活:心跳机制原理与Python自动化实现
  • 2026年Q2:不锈钢防雨箱/临时配电箱/动力配电箱/医院配电柜/厂房配电柜/小区配电箱/尺寸定制配电柜/工业控制柜/选择指南 - 优质品牌商家
  • CAN-TP网络层参数配置避坑指南:N_Bs/N_Cr/STmin设置不当引发的那些‘灵异’故障
  • 参数化设计转换架构:AEUX如何实现设计到动效工作流的300%效率提升
  • LVGL8.3图像控件lv_img实战:从C数组到文件加载,手把手教你搞定嵌入式UI图片显示
  • 生成式AI内容安全防护:NVIDIA NeMo Guardrails实战解析
  • springboot+vue3的在线教育资源管理系统的设计与实现
  • Android 14开发调试遇阻?手把手教你用vdc命令解决adb remount报错
  • 学习python 的while循环嵌套
  • FPGA做信号处理,为什么我推荐你用FIR IP核而不是自己写RTL?聊聊资源与性能的权衡
  • 体验式强化学习:高效训练智能体的核心技术解析
  • 如何为永久在线的CRM网站配置大模型智能客服接口
  • LangGraph.js:现代AI智能体编排框架的设计哲学与实践指南
  • 别再手动一篇篇找了!用Python+Sci-Hub批量下载论文,附最新可用域名获取方法
  • Dify 2026 API网关安全加固实战指南(2024 Q3最新FIPS 140-3合规配置清单)
  • 从vsctoix到EditorToIX:跨编辑器扩展架构设计与工程实践
  • 大语言模型幻觉检测技术解析与FaithLens实践
  • springboot+vue3的校园服务平台的设计与实现
  • MoE架构中的专家阈值路由:动态负载平衡技术解析
  • Wayon维安mos管原厂原装一级代理分销经销
  • 读研必须掌握的技能:文献检索、科研绘图
  • TC397的看门狗不止防复位?深入SMU报警机制与系统安全设计
  • 车载蓝牙技术开发:从协议到实现与面试指南
  • 终极macOS清理指南:用Pearcleaner彻底释放磁盘空间,告别应用残留!
  • 基于MCP协议的AI智能体数据库连接工具sqltools_mcp实战指南
  • 收藏!Web安全隐形杀手——逻辑漏洞 程序员_小白必学安全攻防知识
  • 在aarch64机器上用DBeaver访问虚谷数据库