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

Qt:NULL与nullptr的区别(手写nullptr)

前言

发现还是有人不知道NULLnullptr的区别,故写此文章。

正文

对于NULL

先看NULL的源码
在这里插入图片描述

我们可以看出这段代码是一个典型的预处理器宏定义块,用于处理 NULL 宏的定义。
先看开头

#if defined (_STDDEF_H) || defined (__need_NULL)
  • 这行代码检查是否已经定义了 _STDDEF_H__need_NULL
    • _STDDEF_H 通常是在包含标准库头文件 <stddef.h> 时定义的宏。
    • __need_NULL 是某些情况下(例如,部分标准头文件仅需要 NULL 定义)会定义的宏。

如果其中一个宏已定义,则代码块会执行。


#undef NULL		/* in case <stdio.h> has defined it. */
  • #undef NULL 取消之前可能在其他头文件(例如 <stdio.h>)中定义过的 NULL,确保后续的定义不会发生冲突。

#if defined(__GNUG__) && __GNUG__ >= 3
#define NULL __null
  • 这一部分是针对 GNU C++ 编译器(G++)版本 3 及以上的特定定义。
    • __GNUG__ 是 G++ 编译器的特定宏,用于检测当前编译器是否为 G++。
    • __null 是 G++ 3.x 及以上版本中的内部表示,用于专门处理空指针的情况。

如果符合条件(G++ 3.x 及以上版本),NULL 被定义为 __null


#else   /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
  • 如果当前编译环境不是 G++ 3.x 或更新版本,且不是 C++,那么 NULL 被定义为 ((void *)0)
    • 在 C 语言中,NULL 通常定义为 (void *)0,表示一个空指针。

#else   /* C++ */
#ifndef _WIN64
#define NULL 0
  • 进入了 C++ 的条件分支。
    • 如果正在编译 C++ 代码,且不是 64 位 Windows 环境(未定义 _WIN64),则 NULL 定义为 0
    • 在 C++ 中,NULL 通常直接定义为 0,因为 C++ 允许整数 0 被隐式转换为指针类型。

#else
#define NULL 0LL
#endif  /* W64 */
#endif  /* C++ */
  • 如果是在 64 位 Windows 环境中,NULL 定义为 0LL,表示一个 64 位的长整数常量(long long)。
    • 在 64 位环境中,使用 0LL 以保证 NULL 的大小与指针的大小一致。

#endif  /* G++ */
#endif  /* NULL not defined and <stddef.h> or need NULL.  */
  • 关闭之前的条件编译语句。
  • #endif 对应前面的 #if,表示条件编译的结束。

#undef	__need_NULL
  • 这行代码取消定义 __need_NULL,确保之后不会再次使用这个宏。

总结

  • 在 GNU C++ 3.x 及以上版本中,NULL 定义为 __null
  • 在 C 语言中,NULL 定义为 (void *)0,表示空指针。
  • 在 C++ 中,NULL 定义为 0,因为 0 可以隐式转换为指针类型。
  • 在 64 位 Windows 环境下,为了确保指针和 NULL 的大小一致,定义为 0LL
  • 由以上我们可知NULL既可以表示0又可以表示(void*)0,这就说明它有二义性。

测试

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    int* ptr = NULL;
    int b = 0;
    int* ptr2 = nullptr;
    QString* ptr3 = nullptr;
    if (ptr == 0) {
        qDebug()<<"ptr is null"<<ptr;
    }
    if (b == NULL) {
        qDebug()<<"b is null"<<b;
    }
    if (ptr2 == 0) {
        qDebug()<<"ptr2 is nullptr"<<ptr2;
    }
    if (!ptr3) {
        qDebug()<<"ptr3 is nullptr"<<ptr3;
    }
    return a.exec();
}
运行结果

在这里插入图片描述

对于nullptr

我并没有找到它的源码(如果有找到的兄弟麻烦评论区告知下呗),但是我知道nullptr是一种新的数据类型,就是专门用来表示的一种数据类型。在源码中我只看到它简略的说明
在这里插入图片描述
nullptr的出现就是为了解决NULL即表示0又表示(void*)0的二义性问题。

手写nullptr

const class nullptr_t
{
public:
    // 模板类型转换运算符,可以将 nullptr 转换为任意类型的指针
    template<class T>
    inline operator T*() const
    {
        return 0;  // 返回值为 0,表示空指针
    }

    // 模板类型转换运算符,可以将 nullptr 转换为任意类成员指针
    template<class C, class T>
    inline operator T C::*() const
    {
        return 0;  // 返回值为 0,表示空成员指针
    }
 
private:
    // 重载地址取运算符(&),防止获取 nullptr 的地址
    void operator&() const;
    
} nullptrSelf = {};

使用:

// widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
const class nullptr_t
{
public:
    // 模板类型转换运算符,可以将 nullptr 转换为任意类型的指针
    template<class T>
    inline operator T*() const
    {
        return 0;  // 返回值为 0,表示空指针
    }

    // 模板类型转换运算符,可以将 nullptr 转换为任意类成员指针
    template<class C, class T>
    inline operator T C::*() const
    {
        return 0;  // 返回值为 0,表示空成员指针
    }

private:
    // 重载地址取运算符(&),防止获取 nullptr 的地址
    void operator&() const;

} nullptrSelf = {};


class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void test(int* a);

private:
    void* memberPtr;
};
#endif // WIDGET_H

// widget.cpp


#include "widget.h"
#include "qdebug.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
  memberPtr = nullptrSelf;
  qDebug()<<"memberPtr:"<<memberPtr;
  int* a = nullptrSelf;
  test(a);

}

Widget::~Widget()
{
}

void Widget::test(int* a)
{
    if (a == nullptrSelf) {
        qDebug()<<"a is null "<<a;
    } else {
        qDebug()<<"a is not null "<<a;
    }
}


输出结果

在这里插入图片描述

小结

请指正


http://www.kler.cn/news/318164.html

相关文章:

  • 数据处理与统计分析篇-day10-Matplotlib数据可视化
  • Leetcode 每日一题:Diameter of Binary Tree
  • DataWhale X 南瓜书学习笔记 task03笔记
  • vue3+Element-plus el-input 输入框组件二次封装(支持金额、整数、电话、小数、身份证、小数点位数控制,金额显示中文提示等功能)
  • rust属性宏
  • HTML段落,换行,水平线标签与其属性
  • c/c++八股文
  • MySQL 生产环境性能优化
  • 使用分布式调度框架时需要考虑的问题——详解
  • python 实现 P-Series algorithm算法
  • Seamless:Facebook推出的跨语言语音识别/翻译/合成大模型
  • 计算总体方差statistics.pvariance()
  • 通信工程学习:什么是VNF虚拟网络功能
  • 海思Hi3559av100 sdk开发环境搭建
  • 面试金典题2.3
  • 引用和指针的区别
  • canvas绘制线段、矩形、圆形、文字、贝塞尔曲线、图像、视频处理、线性渐变、径向渐变、坐标变化,旋转,缩放,图形移动
  • 使用数据基础描述进行连续变量的特征提取
  • MySQL数据库索引、事务和存储引擎管理
  • Java基础知识扫盲
  • 代码随想录Day 53|题目:110. 字符串接龙、105.有向图的完全可达性、106. 岛屿的周长
  • Taro多端统一开发解决方案
  • 深入理解LLM的可观测性
  • 31. RabbitMQ顺序消费
  • HarmonyOS NEXT:解密从概念到实践的技术创新与应用前景
  • 解决配置文件中有spring.profiles.active = “@spring.profiles.active@“但是读取不到生效的配置文件的问题
  • pg入门17—如何查看pg版本
  • yolo介绍
  • Python画笔案例-059 绘制甩曲彩点动图
  • Linux下搭建iSCSI共享存储-Tgt