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. vuex state使用

    访问vuex中的state值 方式1 <div>{{$store.state.count}}</div> 方式2 <template> <div id=&qu ...

  2. TypeError: sequence item 1: expected str instance, int found

    Error Msg Traceback (most recent call last): File "E:/code/adva_code/my_orm.py", line 108, ...

  3. NIO原理及案例使用

    什么是NIO Java提供了一个叫作NIO(New I/O)的第二个I/O系统,NIO提供了与标准I/O API不同的I/O处理方式.它是Java用来替代传统I/O API(自Java 1.4以来). ...

  4. vue.js实战——升级版购物车

    HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  5. C#中字符串的字面值(转义序列)

    在程序开发中,经常会碰到在字符串中字面值中使用转义序列,下面表格收集了下转义序列的完整列表,以便大家查看引用: 转义序列列表 转义序列 产生的字符 字符的Unicode值 \' 单引号 0x0027 ...

  6. 使用mongo-express管理mongodb数据库

    前面的话 本文将详细介绍一款用nodejs开发的基于Web的mongodb数据库管理工具mongo-express 安装 首先,全局安装 mongo-express 包 npm install -g ...

  7. Spring MVC 使用介绍(七)—— 注解式控制器(三):生产者与消费者模型

    一.MIME类型 MIME类型格式:type/subtype(;parameter)? type:主类型,任意的字符串,如text,如果是*号代表所有 subtype:子类型,任意的字符串,如html ...

  8. 使用jar包格式化Docker 容器日志

    前面使用JS格式化textarea中的日志内容,但局限于JS语言性能,在日志内容较多时效率无法接受,建议日志内容大于5000行时转投本java程序,文末提供jar包下载. LogsFormat.jav ...

  9. python 高阶函数之 map

    以例子来理解 用法1:如函数 f(x) = x * x,用python实现如下 >>> def f(x): ... return x * x >>> r = map ...

  10. React Native——react-navigation的使用

    在 React Native 中,官方已经推荐使用 react-navigation 来实现各个界面的跳转和不同板块的切换. react-navigation 主要包括三个组件: StackNavig ...