C与C++中空指针的区别

在C里面,由于处处都要使用指针,所以导致NULL遍布各地。我们先来看C99是怎么定义NULL的:

NULL can be defined as any null pointer constant. Thus existing code can retain definitions of NULL as 0 or 0L, but an implementation may also choose to define it as (void*)0. This latter form of definition is convenient on architectures where sizeof(void*) does not equal the size of any integer type. It has never been wise to use NULL in place of an arbitrary pointer as a function argument, however, since pointers to different types need not be the same size. The library avoids this problem by providing special macros for the arguments to signal, the one library function that might see a null function pointer.

可见,在C99里面,NULL可以被定义为0或者0L(32位和64位的区别),或者直接就是由0或者0L转成的成void*。
 
接下来我们来看下C++ 14(N4296)中所定义的null pointer。

A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t.

 

A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4). A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value. —end note ]

 

A prvalue of type “pointer to cv T,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a non-null pointer value of a pointer to object type to a “pointer to cv void” represents the address of the same byte in memory as the original pointer value. The null pointer value is converted to the null pointer value of the destination type.

 

A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

 
第一句话就表明了,在C++中,一个指向空的指针要么是一个字面值整形,要么是一个std::nullptr_t
 
我们再来看VS 2015 中所定义的NULL,就是一个0
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
 
用nullptr解决C++中NULL所不能解决的问题

前面我们说了,C++中的NULL,其实就是一个0,这会导致很多问题,比如我们可以写一个函数重载:
#include <iostream>
#include <algorithm>
#include <memory>
void fun(int)
{
std::cout << "fuck1" << std::endl;
}
void fun(void *)
{
std::cout << "fuck2" << std::endl;
}
int main(int argc, char *argv[])
{
fun(NULL);
system("pause");
return ;
}
 
一般来说,我们传进去一个NULL,一般想的是要传一个指针,可是在上面的程序中,我们却调用的是int的版本。
 
但是当我们传的是nullptr时:
int main(int argc, char *argv[])
{
fun(nullptr);
system("pause");
return ;
}
 
这个时候调用的是第二个版本了,符合我们的设想,这是因为C++规定nullptr可以转为指针类型。而且是cv void *
 
再来一个例子,也就是我们最常见的模板匹配问题了:
struct Fuck
{
Fuck(char *){ }
};
int main(int argc, char *argv[])
{
auto p = std::make_shared<Fuck>(NULL);
throwing();
system("pause");
return ;
}
 
这个代码会报错,至于为什么,我们先来分析一下make_shared的模板:
template<class _Ty,
class... _Types> inline
shared_ptr<_Ty> make_shared(_Types&&... _Args)
{ // make a shared_ptr
_Ref_count_obj<_Ty> *_Rx =
new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);
shared_ptr<_Ty> _Ret;
_Ret._Resetp0(_Rx->_Getptr(), _Rx);
return (_Ret);
}
// TEMPLATE CLASS _Ref_count_obj
template<class _Ty>
class _Ref_count_obj
: public _Ref_count_base
{ // handle reference counting for object in control block, no allocator
public:
template<class... _Types>
_Ref_count_obj(_Types&&... _Args)
: _Ref_count_base()
{ // construct from argument list
::new ((void *)&_Storage) _Ty(_STD forward<_Types>(_Args)...);
}
_Ty *_Getptr() const
{ // get pointer
return ((_Ty *)&_Storage);
}
private:
virtual void _Destroy() _NOEXCEPT
{ // destroy managed resource
_Getptr()->~_Ty();
}
virtual void _Delete_this() _NOEXCEPT
{ // destroy self
delete this;
}
typename aligned_union<, _Ty>::type _Storage;
};
 
这里多说几句,make_shared的操作是先给_Ref_count_obj<_Ty>类型分配一块内存,然后再placement new,回想一下我们平常使用shared_ptr的时候,都是shared_ptr<T> foo(new T(arg...))这样用的,但是其实用make_shared创建shared_ptr的方法更为高效,因为我们从模板中可以看到shared_ptr的占用空间其实是要比T要大的(为了保存引用计数的东西)。如果我们使用shared_ptr<T> foo(new T(arg...))来构造shared_ptr,那么要先给T分配内存并构造T,然后在分配ref_count的内存,但是如果使用make_shared,那么就会直接给T和ref_count一起分配内存,然后再通过C++11的完美转发把T的构造函数传给make_shared。
 
好现在回到我们这篇博客的主题,为什么传一个NULL会报错呢?这是因为由于C++的NULL就是一个字面值常量0,所以传进去时,会被forward推断成int &&,int &&与char *当然不是一个东西,就会报错。
 
这个时候我们就必须使用nullptr了,nullptr可以转换成void *,然后再隐式转换成char *
auto p = std::make_shared<Fuck>(nullptr);
 
 
 

C++ NULL与nullptr的区别的更多相关文章

  1. (转)null和NULL和nullptr和””区别

    突然想到这个有趣的问题:C语言和C++对大小写是敏感的,也就是说null和NULL是区别对待的.NULL代表空地址,null只是一个符号.便来深究,看了很多资料,总结如下: 其实null和NULL都是 ...

  2. NULL和nullptr的区别

    //error C2665: “go”: 2 个重载中没有一个可以转换所有参数类型 #include <iostream> void go(int num) { std::cout < ...

  3. 【转载】C/C++杂记:NULL与0的区别、nullptr的来历

    原文:C/C++杂记:NULL与0的区别.nullptr的来历 某些时候,我们需要将指针赋值为空指针,以防止野指针.   有人喜欢使用NULL作为空指针常量使用,例如:int* p = NULL;. ...

  4. 再谈NULL和nullptr(C++11)区别

    在谈NULL和nullptr区别之前,我们先看段代码: #include "stdafx.h" #include <iostream> using namespace ...

  5. 字符串怎么换行 || 字符串中使用单引号时应该怎么写 || 保留两位小数 || 数字0在if中的意思是false || 什么情况下会会报undefined || null和undefined的区别 ||

    换行的字符串 "This string\nhas two lines" 字符串中使用单引号时应该怎么写 'You\'re right, it can\'t be a quote' ...

  6. MySQL 中NULL和空值的区别

    平时我们在使用MySQL的时候,对于MySQL中的NULL值和空值区别不能很好的理解.注意到NULL值是未知的,且占用空间,不走索引,DBA建议建表的时候最好设置字段是NOT NULL 来避免这种低效 ...

  7. tips null和undefined的区别

    tips null和undefined的区别 1.undefined类型 undefined类型只有一个值,即特殊的undefined.在使用var声明变量但未对其加以初始化时,这个变量的值就是und ...

  8. Convert和Parse对null值处理的区别

    类型的转换在日常的变成中是经常被用到的,我们最常用的类型转换的方法就是Convert和Parse, 下面来说一下这两者null值处理的区别. int i1 = Convert.ToInt32(null ...

  9. [BS-22] Objective-C中nil、Nil、NULL、NSNull的区别

    Objective-C中nil.Nil.NULL.NSNull的区别 1.定义: nil:      OC语言定义:#define nil __DARWIN_NULL   /  #define __D ...

随机推荐

  1. vue父子组件路由传参的方式

    一.get方式(url传参): 1.动态路由传参: 父组件: selectItem (item) { this.$router.push({ path: `/recommend/${item.id}` ...

  2. tinkphp5使用中碰到的问题 持续更新

    1.使用助手函数(如controller(),model(),validate())进行实例化时只需要引入think\Controller或think\Model或think\Validate即可,无 ...

  3. 服务器安装docker后免除sudo命令

    1. 先建立一个docker组:sudo groupadd docker 2. 将用户加入docker组:sudo usermod -aG docker (用户名) 3. 先退出登录:exit 4. ...

  4. Luogu P2341 [HAOI2006]受欢迎的牛 SCC缩点

    把强连通分量缩点,如果有且仅有一个出度为0的强连通分量,那么答案就是他的size:如果有多个入度为0的,那么没有明星牛. #include<cstdio> #include<iost ...

  5. Codeforces 161E(搜索)

    要点 标签是dp但搜索一发就能过了. 因为是对称矩阵所以试填一下就是一个外层都填满了,因此搜索的深度其实不超过5. 显然要预处理有哪些素数.在这个过程中可以顺便再处理出一个\(vector:re[le ...

  6. NET full stack framework

    NFX UNISTACK 介绍 学习.NET Core和ASP.NET Core,偶然搜索到NFX UNISTACK,现翻译一下Readme,工程/原文:https://github.com/aumc ...

  7. 这个匿名对象没有实现IComparable接口

    https://www.cnblogs.com/felixnet/p/5193086.html https://docs.microsoft.com/zh-cn/dotnet/api/system.i ...

  8. #1369 : 网络流一·Ford-Fulkerson算法 模板题

    http://hihocoder.com/problemset/problem/1369?sid=1108721 别人都说先学网络流再学二分图,但是我先学了二分图的,感觉网络流好高端啊. 首先对于原图 ...

  9. C. Epidemic in Monstropolis

    http://codeforces.com/contest/733/problem/C 一道很恶心的模拟题. 注意到如果能凑成b[1],那么a的前缀和一定是有一个满足是b[1]的,因为,如果跳过了一些 ...

  10. 算法导论课后习题解答 第一部分 练习1.1-1->1.1-5

    很高兴能和大家一起共同学习算法导论这本书.笔者将在业余时间把算法导论后面的题解以博文的形式展现出来希望能得到大家的支持谢谢.如果有可能我会做一些教学视频免费的供大家观看. 练习题选自算法导论中文第三版 ...