C的NULL

在C语言中,我们使用NULL表示空指针,也就是我们可以写如下代码:

int *i = NULL;
foo_t *f = NULL;

实际上在C语言中,NULL通常被定义为如下:

#define NULL ((void *)0)

也就是说NULL实际上是一个void *的指针,然后吧void *指针赋值给int *和foo_t *的指针的时候,隐式转换成相应的类型。而如果换做一个C++编译器来编译的话是要出错的,因为C++是强类型的,void *是不能隐式转换成其他指针类型的,所以通常情况下,编译器提供的头文件会这样定义NULL:

#ifdef __cplusplus ---简称:cpp c++ 文件
#define NULL 0
#else
#define NULL ((void *)0)
#endif

C++的0

因为C++中不能将void *类型的指针隐式转换成其他指针类型,而又为了解决空指针的问题,所以C++中引入0来表示空指针(注:0表示,还是有缺陷不完美),这样就有了类似上面的代码来定义NULL。实际上C++的书都会推荐说C++中更习惯使用0来表示空指针而不是NULL,尽管NULL在C++编译器下就是0。为什么C++的书都推荐使用0而不是NULL来表示空指针呢?我们看一个例子:

在foo.h文件中声明了一个函数:

void bar(sometype1 a, sometype2 *b);

这个函数在a.cpp、b.cpp中调用了,分别是:

a.cpp:

bar(a, b);

b.cpp:

bar(a, );

好的,这些代码都是正常完美的编译运行。但是突然在某个时候我们功能扩展,需要对bar函数进行扩展,我们使用了重载,现在foo.h的声明如下:

void bar(sometype1 a, sometype2 *b);
void bar(sometype1 a, int i);

这个时候危险了,a.cpp和b.cpp中的调用代码这个时候就不能按照期望的运行了。但是我们很快就会发现b.cpp中的0是整数,也就是在overload resolution的时候,我们知道它调用的是void bar(sometype1 a, int i)这个重载函数,于是我们可以做出如下修改让代码按照期望运行:

bar(a, static_cast<sometype2 *>());  --- 我们的游戏项目就遇到这个问题,这样用开起来别扭

我知道,如果我们一开始就有bar的这两个重载函数的话,我们会在一开始就想办法避免这个问题(不使用重载)或者我们写出正确的调用代码,然而后面的这个重载函数或许是我们几个月或者很长一段时间后加上的话,那我们出错的可能性就会加大了不少。貌似我们现在说道的这些跟C++通常使用0来表示空指针没什么关系,好吧,假设我们的调用代码是这样的:

foo.h

void bar(sometype1 a, sometype2 *b);

a.cpp

bar(a, b);

b.cpp

bar(a, NULL);

当bar的重载函数在后面加上来了之后,我们会发现出错了,但是出错的时候,我们找到b.cpp中的调用代码也很快可能忽略过去了,因为我们用的是NULL空指针啊,应该是调用的void bar(sometype1 a, sometype2 *b)这个重载函数啊。实际上NULL在C++中就是0,写NULL这个反而会让你没那么警觉,因为NULL不够“明显”,而这里如果是使用0来表示空指针,那就会够“明显”,因为0是空指针,它更是一个整形常量。

在C++中,使用0来做为空指针会比使用NULL来做空指针会让你更加警觉。

C++ 11的nullptr

虽然上面我们说明了0比NULL可以让我们更加警觉,但是我们并没有避免这个问题。这个时候C++ 11的nullptr就很好的解决了这个问题,我们在C++ 11中使用nullptr来表示空指针,这样最早的代码是这样的,

foo.h

void bar(sometype1 a, sometype2 *b);

a.cpp

bar(a, b);

b.cpp

bar(a, nullptr);

在我们后来把bar的重载加上了之后,代码是这样:

foo.h

void bar(sometype1 a, sometype2 *b);
void bar(sometype1 a, int i);

a.cpp

bar(a, b);

b.cpp

bar(a, nullptr);

这时候,我们的代码还是能够如预期的一样正确运行。

在没有C++ 11的nullptr的时候,我们怎么解决避免这个问题呢?我们可以自己实现一个(《Imperfect C++》上面有一个实现):

const
class nullptr_t
{
public:
template<class T>
inline operator T*() const
{ return ; } template<class C, class T>
inline operator T C::*() const
{ return ; } private:
void operator&() const;
} nullptr = {};

虽然这个东西被大家讨论过很多次了,但是我觉得还是有必要再讨论一下,毕竟在C++ 11还没有普及之前,我们还是应该知道怎么去避免问题,怎么很快的找到问题。

------  结论:  如果使用 nullptr 初始化对象,就能避免 0 指针的二义性的问题。  cocos2d-x3.0 采用 c++11的新特性了... c++ 二春来了...

史上最明白的 NULL、0、nullptr 区别分析(老师讲N篇都没讲明白的东东),今天终于明白了,如果和我一样以前不明白的可以好好的看看...的更多相关文章

  1. 【Tips】史上最全H1B问题合辑——保持H1B身份终级篇

    [Tips]史上最全H1B问题合辑——保持H1B身份终级篇 2015-04-10留学小助手留学小助手 留学小助手 微信号 liuxue_xiaozhushou 功能介绍 提供最真实全面的留学干货,帮您 ...

  2. 聊一聊c++中指针为空的三种写法 ----->NULL, 0, nullptr

    看到同事用了一下nullptr.不是很了解这方面东东,找个帖子学习学习 http://www.cppblog.com/airtrack/archive/2012/09/16/190828.aspx N ...

  3. .Net魔法堂:史上最全的ActiveX开发教程——ActiveX与JS间交互篇

    一.前言 经过上几篇的学习,现在我们已经掌握了ActiveX的整个开发过程,但要发挥ActiveX的真正威力,必须依靠JS.下面一起来学习吧! 二.JS调用ActiveX方法 只需在UserContr ...

  4. 史上最简单的的HashTable源码分析

    HashTable源码分析 1.前言 Hashtable 一个元老级的集合类,早在 JDK 1.0 就诞生了 1.1.摘要 在集合系列的第一章,咱们了解到,Map 的实现类有 HashMap.Link ...

  5. 怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏

    这么说吧,在我眼里,Java 就是最流行的编程语言,没有之一(PHP 往一边站).不仅岗位多,容易找到工作,关键是薪资水平也到位,不学 Java 亏得慌,对吧? 那可能零基础学编程的小伙伴就会头疼了, ...

  6. linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】

    转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 预备知识 lin ...

  7. 史上最难的一道Java面试题 (分析篇)

    博客园 匠心零度 转载请注明原创出处,谢谢! 无意中了解到如下题目,觉得蛮好. 题目如下: public class TestSync2 implements Runnable { int b = 1 ...

  8. 【UEFI】---史上最全的X86平台启动流程分析(软硬结合)

    最近研究了下X86处理器的启动流程分析,相比于常见的ARM来说,X86平台启动流程真的复杂了很多,本次基于项目实际的两个问题入手,研究了包括以下几个部分的内容: 1. 从硬件角度看启动流程 2. 从软 ...

  9. .Net魔法堂:史上最全的ActiveX开发教程——自动更新、卸载篇

    一.前言 B/S模式的特点之一,客户端版本升级相对简单.快捷,适合产品的快速迭代.而ActiveX组件的自动更新同样也继承了这一优点.下面我们一起来了解吧! 二.二话不说更新ActiveX 1. 设置 ...

随机推荐

  1. 关于h5绘制canvas生成图片的注意点!

    1.第一个是关于移动端自适应的问题: 答:如果是最后只要一张canvas生成的图片,而不是要绘制的canvas的图形,则不需要考虑自适应,绘制canvas的时候的宽高,可以直接写成UI提供的图的大小, ...

  2. MySQL中的float和decimal类型有什么区别

    decimal 类型可以精确地表示非常大或非常精确的小数.大至 1028(正或负)以及有效位数多达 28 位的数字可以作为 decimal类型存储而不失其精确性.该类型对于必须避免舍入错误的应用程序( ...

  3. java多线程中 volatile与synchronized的区别-阿里面试

    volatile 与 synchronized 的比较(阿里面试官问的问题) ①volatile轻量级,只能修饰变量.synchronized重量级,还可修饰方法 ②volatile只能保证数据的可见 ...

  4. php如何解决中文乱码问题?

    为什么会出现中文乱码? 很多新手朋友学习PHP的时候,发现程序中的中文在输出的时候会出现乱码的问题,那么为什么会出现这种乱码的情况呢?一般来说,乱码的出现有2种原因,一种是由于编码(charset) ...

  5. luogu2597-[ZJOI2012]灾难 && DAG支配树

    Description P2597 [ZJOI2012]灾难 - 洛谷 | 计算机科学教育新生态 Solution 根据题意建图, 新建一个 \(S\) 点, 连向每个没有入边的点. 定义每个点 \( ...

  6. Vuex、axios、跨域请求处理和import/export的注意问题

    一.Vuex 1.介绍 vuex是一个专门为Vue.js设计的集中式状态管理架构. 对于状态,我们把它理解为在data中需要共享给其他组件使用的部分数据. Vuex和单纯的全局对象有以下不同: 1. ...

  7. 关于ESB(企业服务总线)的学习笔记

    MQ(消息队列 message queues),它是一种应用程序对应用程序的通信方法.排队指的是应用程序通过队列来通信.队列的使用除去了接收和发送应用程序同时执行的要求. Web Sevice 技术, ...

  8. 【nginx】nginx日常命令

    看下nginx命令的帮助信息 [root@localhost oa_workflow_test]# /usr/local/nginx/sbin/nginx -h nginx version: ngin ...

  9. Mycat的读写分离

    1. Mycat实现读写分离的部署: https://www.cnblogs.com/softidea/p/5447566.html springboot动态数据源的原理以及配置: Spring内置了 ...

  10. IDEA配置注释模板

    直接进入主题: Ctrl+Alt+S进入设置界面(我没改过按键映射,你也可以从File-OtherSetting进入设置),找到Editor->File and Code Templates,先 ...