f (p == NULL), if (p == 0) 还是 if(p) ?----再论空指针检测问题
某些C/C++编程的书中,曾经提到如何判断指针是否为空的问题.很显然,if (p == NULL), if (p == 0) 和if(p),都能够完成这一任务,差别在于可读性方面.我们分别加以讨论.
1. if (p == NULL)
相当多的文章建议采用,他们中的部分人甚至认为,其他做法都是错误的.这个形式一个变种是 if (NULL == p),虽然略显怪异,但是,万一我们误将==写成了=,编译器通常都会给出编译错误,从而防止键入的错误.拥护这一方案的人认为,NULL可以明确地表达出p 是一个指针,而不是整数或其他什么东西.有助于我们理解代码.然而,反对者也众多,包括C++之父B.S.反对的主要根据是,NULL并不是语言的一部分,通常,它是由一个宏定义而来的:
#define NULL ((void*)0)
一般来说,NULL其实可以工作的很好,但是,宏的出身,让它不让人喜欢.另外,它也确实有些现实的问题,典型的,成员指针的问题.假设有类型A,
typedef void (A::*FUNT)();
现在,FUNT定义成一个成员函数指针类型了.假设有FUNT fun = ...
对于if (fun == NULL)将会导致编译失败.原因在于FUNT和void*之间不存在类型转换.不仅仅成员函数存在这个问题,普通函数指针也可能存在这个问题.
那么,我们改变NULL的定义呢?尝试这样定义:
#define NULL (0)
或者
#define NULL 0
这也可能存在问题.NULL这个名字意味着自己是个指针,只允许指针和NULL比较,然而事实上却并非如此.这有欺骗的嫌疑.许多人认为,欺骗不可接受,暴露问题比欺骗要好得多.这种欺骗导致编译器无法提供恰当的帮助.
2. if (p == 0)
包括B.S在内的许多人也推荐这种方式----尽管他们大多认为这个方案并不完美.首先,语言内部对于常量0有特殊的处理,因此,第一种选择中可能遇到的和函数或成员函数指针比较失败的问题可以解决.另一方面,它没有强调自己必须和指针比较,因此,也不存在欺骗.而且,因为这种方式受语言支持,每个C++ 程序员都会熟悉这一方法,而不会觉得难以理解.并且,一个新的语言改进正在进行,会为语言提供一个nullptr的关键字,专门用来和各种指针比较.但目前的语言实现基本上还不支持这一特性.
3.if (p)
许多C++的铁杆或资深用户对前两个方案嗤之以鼻.他们认为这个方案正确,简单,有效. 另外,这一方案可以有效地应用到所谓smart_ptr上去.而1和2方案往往会带来潜在的不安全因素.假设,我们现在有一个smart_ptr类,为了支持第一种语法,我们需要为之定义一个全局的比较函数.但是,这也意味着要提供一系列长相难看(参数类型不对称)的重载,而且,需要花费很多精力.并且,难以提供一个完全一致的实现.
我们看一下,为了支持smart_ptr,不同方案分别需要做些什么.
为方案1,我们要定义:
bool operatro ==( const type& L, void* null);
bool operatro ==( void* null, const type& R);
同样,还需要重载operator !=;
这里的问题是,smart_ptr我们本来不指望和任何void指针比较相等的,但是现在不得不允许了.也就是说,我们可以和一个非NULL的void指针比较了.而我们的本意是:查询这个指针是不是空.
方案2存在同样的问题,而且更严重.我们可能和任意整数比较了.而对于普通的指针,和非0常量比较会编译失败的.
另一个手段,借助于隐式转换,但是那更加不安全.
在第三个方案中,我们通常会重载operator ! 和operator bool.虽然重载operator bool也是危险的,但是现在技术上可以绕过这一点,就是重载一个unspecified_boolean的办法,类似实现如下:
template <typename Derived>
class unspecified_boolean_imp
{
struct Tester{ void dummy(){} };
typedef void (Tester::*unspecified_boolean_type)();
public:
operator unspecified_boolean_type() const THROW0() {
return static_cast <const Derived&> (*this).operator!() ? 0 : &Tester::dummy;
}
protected:
unspecified_boolean_imp(){}
~unspecified_boolean_imp(){};
};
这样,我们可以安全使用if (p)的语法而不必担心被误用.
第三个方案常见于GP代码.然而,拥护1,2方案的人往往反对,认为3不利于阅读,过于技巧化.而3的拥护者认为,3明显是一种更务实有效的做法,可以使得实现简单明了,而且牢靠.
那么回顾一下我们的意图:查询指针是不是空.那么为什么不提供一个查询的函数呢?把差异封装掉不是很好?
于是作下述尝试:
if (is_nullptr(p));
嗯,我觉得这样的代码比if (p == NULL)更加直白.如果is_nullptr定义成宏,可以这样提供:
#define IS_NULLPTR(x) ((x) == 0)
可是,我不喜欢宏,而且,我觉得,宏能够帮我做的事情太少,例如,如果p是一个int,编译器无声无息地成功.我觉得,很明显地is_nullptr对于非指针类型应该给个编译错误.事情落到了template头上:
template <typename T> bool is_nullptr(T* p){
return !p;
}
现在,我可以安心地写if (is_nullptr(p))了,如果p是int类型,会编译出错.但是,还有两个问题没有解决.一个是smart_ptr,另一个是成员函数指针. smart_ptr其实很简单,重载就是了
template <typename T>
bool is_nullptr(const smart_ptr <T> & p){ return !p;}
借助于traits技术来维护concept,事实上,可以做得更好,我们只需要一行额外代码,在smart_ptr的定义中加入:
enum { is_pointer = 1};
就足矣.即使我们无法修改smart_ptr,我们仍然有后路,不是吗?如果第三方的smart_ptr不支持if (p)这样的语法,但是,通过重载is_nullptr,我们可以为所有的可能提供唯一的使用形式.我喜欢这种简单性.
函数指针不需要我们做特别的处理就已经支持了.现在处理成员函数指针:
template <typename T, typename U>
bool is_nullptr( T U::* x)
{
return !x;
}
幸运的是,它可以工作.无论是针对成员函数还是成员数据.而且,也无视函数的参数列表.曾经在实现变长template参数地狱中幸存的人们,是不是觉得世界太不公平了?^_^
对于函数,这里的T不仅仅是返回值,实际上是一个函数定义.不过,我不确定这是否确实符合ISO标准.不过没关系,大不了重回地狱,I will be back!
你可以批评我的巴洛克倾向,我要开始享受静态类型安全的is_nullptr了.
[解决办法]
好文,学习了
[解决办法]
up
[解决办法]
学习
[解决办法]
学习学习
[解决办法]
喜欢1, 因为可以用search在代码中轻易搜索到,而2和3就很困难。
[解决办法]
不错 马克一下
[解决办法]
学习
[解决办法]
mark!
[解决办法]
mark之
[解决办法]
下
[解决办法]
虽然比较有技巧性,不过还是不可多得的好东西
[解决办法]
sssss
[解决办法]
mark
[解决办法]
秃子就是厉害,呵呵
-
以下说说我的看法
因为单纯的is_nullptr并不能针对所有的类型.
比如,自定义类的处理.还有涉及到浅拷贝的时候,很难捕获这种情况.
class A
{
public:
int *number;
}
A one, two;
one.number = new int(10);
two = one;
因为此类没有自定义拷贝构造函数,使用默认的构造就会造成,
one.number和two.number会指向同一快区域.
使用的方法我能想到的是 在不改变类的设计情况下判断number指针 是否为空.
在这种情况下只能对特定的类进行重载.
[解决办法]
if(p)
[解决办法]
jf
[解决办法]
翻译的还是原创啊
总之学习啦
谢谢楼主~
[解决办法]
好文,如果原创的话,强烈支持!!楼主牛人……
[解决办法]
以前看到过类似文章
我一般都是用
if(NULL==p)
[解决办法]
make,留名,日后再说
[解决办法]
对,正如C++一样,虽然强大但对GUI的 支持很 弱
只能尽可能的完善
[解决办法]
顶一个,虽然常常使用第一种方式却不知有这样大的门道。学习了!
[解决办法]
先 Mark,回去慢慢看
[解决办法]
学习
[解决办法]
学习
[解决办法]
mark
[解决办法]
学习
[解决办法]
细节的处理过程也许更能反映问题 ...
常用 if (p == NULL) ...
[解决办法]
我喜欢用 if ( NULL == p ) ...
[解决办法]
高手
[解决办法]
习惯于 A *p=0;...;if(p)...
不过,楼主算是一个有心人啊。至于lidongri所说的完全可以这样if(is_nullptr(one.number))...
希望能经常看到这样的文章。
[解决办法]
好像不错了样子,先收藏一下,回头看看
[解决办法]
在LZ的blog上已经拜读过了
[解决办法]
好文章。。。
收了。。。
谢谢。。。
顺便接分。。。
[解决办法]
支持第二, 0的特殊意义在机器语言中是显而易见的.
0/1, 0代表不存在, 1代表存在.
[解决办法]
up
[解决办法]
学习
[解决办法]
赞一个
[解决办法]
再学习
[解决办法]
if (p == NULL),if (p == 0)两者
和
if(p)
并不等价呀?
楼主怎么搞的?:)
[解决办法]
好文章,可以拿来做学问!!
不过平时处理问题时,可以收缩问题的领域,
我还是喜欢偷懒使用if(NULL==p)或者if(p)!(可能是C出身的原因!)
[解决办法]
好文章,ding
[解决办法]
学习了~
[解决办法]
为了判断一个指针,要调用一个函数,似乎不值得.好在C++有inline,用C的只能if(p!=0)了.
[解决办法]
mark
[解决办法]
up
[解决办法]
是自己研究的?还是翻译的。不论怎么说,先学习了再说。
[解决办法]
mark+study
[解决办法]
道理是这么讲,不过现在用3种的都不错,就看个人习惯了
[解决办法]
学会了mark
[解决办法]
学习下..
------解决方案--------------------
用if (p == NULL)罢
C++在这点上简直是没事找事,NULL存在这么多年了,非要说不是语言的一部分
现代的编译器对于if (p = NULL)都可以提出警告的,不用写成丑陋的if (NULL == p)
[解决办法]
good
[解决办法]
学习
[解决办法]
连安全bool都拖出来。
简单问题何必复杂化。
讨论回字4种写法也就算了,还得讨论那种写法漂亮。
C++精髓是“实用”,不要太学究了。
这些问题,最好成立一个“专家入门”版来讨论。
[解决办法]
好文,好文啊。
喜欢,很喜欢。凡是用到特化的我都喜欢。
支持。
[解决办法]
mark
[解决办法]
不错的文章 ,UP一下
[解决办法]
小菜的意见,望批评指正!
我觉得使用if(p==0)准确一点。
指针在C++里面是一种基础类型,它本身在内存中是一块具有4字节大小的内存块,这块内存的值也就是该指针的值是另一块内存的地址,既然是内存地址值那么这就是整型的数值,用整型的数值与0做比较意义是比较准确的。
对于if(p==NULL)的情况,NULL一般是用void*定义的宏,这就的看具体编译器是怎么实现的。将NULL定义为空指针就得看NULL的初始化情况,如果是NULL未进行初始化的情况就比较危险了,NULL被定义为指针就意味着在内存中开出4字节的内存块出来,这块内存不进行初始化就很有可能有垃圾数据,这时候你定义一个指针p也没有初始化或者你已经初始化并且正在使用,如果碰上p的值与NULL的值正好相同,虽然这种情况发生的几率很小很小但不排除有发生的可能,如果真的发生了将导致很难找出原因的灾难;如果编译器实现了对NULL的初始化就必然回用整型来初始化,我相信首先会用0,这种情况犹如if(p==0)不过效率还没有if(p==0)来的高,如果用其他值初始化则又回到了上述灾难性的问题。
再谈谈if(p)的问题,虽然这样用是非常简洁易用的,但是不适合初学者。为什么?还是初始化的问题,初学者一般都没有初始化的习惯,刚定义的指针很可能有垃圾数据,只要有数据就是非0,那么if(p)就会成立,那么下一步就会直接拿来用,这时候计算的结果将是不确定的。
还有关于指针的问题,我觉得还是有必要看看林锐博士写的高质量C++编程指南一文,真的很实用,再次感谢林锐博士的无私奉献!
[解决办法]
先收藏
再学习
[解决办法]
学习
[解决办法]
那该怎么办, 我还是会选用(NULL == P)
[解决办法]
这是c++自由的一种体现 关于“茴”字有4种写法。。。。。。
[解决办法]
这种讨论并不是孔乙已式的迂腐,这关系到代码的效率与正确性的问题!
[解决办法]
文章对大家经常用, 但忽略的问题解释的很清楚. 谢谢楼主.
[解决办法]
路过,学习。。
[解决办法]
xinwlx(学无以致用,恨自己不用功!) ( ) 信誉:100 Blog 加为好友 2007-06-15 09:20:36 得分: 0
小菜的意见,望批评指正!
我觉得使用if(p==0)准确一点。
指针在C++里面是一种基础类型,它本身在内存中是一块具有4字节大小的内存块,这块内存的值也就是该指针的值是另一块内存的地址,既然是内存地址值那么这就是整型的数值,用整型的数值与0做比较意义是比较准确的。
对于if(p==NULL)的情况,NULL一般是用void*定义的宏,这就的看具体编译器是怎么实现的。将NULL定义为空指针就得看NULL的初始化情况,如果是NULL未进行初始化的情况就比较危险了,NULL被定义为指针就意味着在内存中开出4字节的内存块出来,这块内存不进行初始化就很有可能有垃圾数据,这时候你定义一个指针p也没有初始化或者你已经初始化并且正在使用,如果碰上p的值与NULL的值正好相同,虽然这种情况发生的几率很小很小但不排除有发生的可能,如果真的发生了将导致很难找出原因的灾难;如果编译器实现了对NULL的初始化就必然回用整型来初始化,我相信首先会用0,这种情况犹如if(p==0)不过效率还没有if(p==0)来的高,如果用其他值初始化则又回到了上述灾难性的问题。
再谈谈if(p)的问题,虽然这样用是非常简洁易用的,但是不适合初学者。为什么?还是初始化的问题,初学者一般都没有初始化的习惯,刚定义的指针很可能有垃圾数据,只要有数据就是非0,那么if(p)就会成立,那么下一步就会直接拿来用,这时候计算的结果将是不确定的。
还有关于指针的问题,我觉得还是有必要看看林锐博士写的高质量C++编程指南一文,真的很实用,再次感谢林锐博士的无私奉献!
=====================
废话一大堆...还讲那么多...
[解决办法]
:-)楼上的,你搞点有用的出来,让我们学习学习撒!
[解决办法]
COPY了~
[解决办法]
up,收藏
------解决方案--------------------
学习学习
[解决办法]
To:xinwlx(学无以致用,恨自己不用功!)
对于LZ的说明和创新,我个人表示赞赏和肯定。
但我觉得这三种表示方式之间的差异只是存在于某个方面而已,对于平常的使用是没有区别的,只是个人的使用习惯而已,我自己比较钟爱于第三种。
至于你所说的,其实并没有我前面所说的那么严重(可能我太激动了,没有控制好我的语气),我只是觉得你说:
“将NULL定义为空指针就得看NULL的初始化情况,如果是NULL未进行初始化的情况就比较危险了,NULL被定义为指针就意味着在内存中开出4字节的内存块出来,这块内存不进行初始化就很有可能有垃圾数据,这时候你定义一个指针p也没有初始化或者你已经初始化并且正在使用,如果碰上p的值与NULL的值正好相同,虽然这种情况发生的几率很小很小但不排除有发生的可能,如果真的发生了将导致很难找出原因的灾难;”
我觉得是你自己多虑了,这种情况不可能发生...
[解决办法]
mark
[解决办法]
Mark
[解决办法]
study
[解决办法]
把简单的东西弄复杂,再把复杂的东西弄得看起来简单
C++……
[解决办法]
好好学习!天天向上!!!
[解决办法]
mark
[解决办法]
如果真的简单,那么就让他简单,没必要复杂化。
如果有可能复杂,那么就让他尽量的复杂,但使用起来要尽量的简单。
如果确实很复杂,那么就要考虑是不是还有其他的方法,也许会有一种方法实际上是非常简单的。
这世界本来就是当你以为简单的时候它很复杂,当你以为复杂的时候却可以很简单。
[解决办法]
good
[解决办法]
mark
[解决办法]
that 's the things I like!
[解决办法]
所以 建议你反过来写 if(NULL == p)...
[解决办法]
void* p;
is_nullptr(p);这个恐怕还需要
bool is_nullptr(void* p){
return !p;
}
lz这种做法要么降低效率,要么增加代码目标文件大小,并非放到哪里都是最佳方案。
而且lz的讨论基于auto_ptr,恰恰有些本末倒置,atuo_ptr开发出来的目的是提供更好的管理,降低开发的负担,auto_ptr应该去迁就,应该尽量封装常用的工作,lz把原因建立在部分结果之上,出发点就有些偏颇了。说白了,lz的方案算是auto_ptr的补充。
[解决办法]
向前辈学习!
[解决办法]
jf仔细研究
[解决办法]
good good study!day day up!
[解决办法]
顶
[解决办法]
“NULL”的最大问题就在于C++标准没有规定它必须被怎样定义,准确地讲,标准中根本没有这个东西。
因此,它应该是0、是(0),是((void*)0),或者是其它的非零地址,都没有标准。(并非每种平台都用0地址处理“空指针”这个概念)。
再有,“NULL”必须在哪个头文件中被定义,当然也同样没有标准。
一个库的设计者是否一定要提供“NULL”这个宏?同样,也没有标准答案。
NULL是该作为宏定义还是作为static const的void*常量?也没有答案。
所以,还有别用为好。
[解决办法]
简单即完美.
没有必要把指针判断搞得那么复杂。
指针本身就是一个值,使用 if(p) 判空完全没有问题。
平台因素,设计因素和语言本身毫无关系。
平台因素:作为高级语言,如果出现非零空指针应该有编译器接管处理,而不是暴露给程序员。指针作为一种语言内建要素,这不难做到。至于设计因素,那就超出语言本身范围了。和LISP这些东西不一样,C++不是一种可以自我扩展的语言。
从语法上说,p是不是指针那并不重要,重要的是if(p)所表达的逻辑 p 非零即真,p 为零即假。
[解决办法]
if( NULL == p) 的表达方法强调p为指针这一特殊属性。
if( p == 0 ) 则强调p的十进制值,不管p的类型。
然而,十进制值值域如果以0位分界线,会出现负值和正值两个域,单从此语句无法判别p类型,但语意上却暗示着这种可能性。
在阅读代码的时候,这两种风格会带来不同的理解和不同的阅读负担。值类型上的误解在大型系统中往往是致命的……
[解决办法]
路过。。。。
[解决办法]
mark
[解决办法]
我倾向于采用最安全的方式
无论那种形式,其实效率差不多,在一个系统中运行起来,这点差别根本不会有任何速度上的影响。
但是,世界上并不只有一种平台,一种CPU,一种操作系统,在嵌入式开发中会有很多稀奇古怪的操作系统和编译器,那里的空指针谁敢肯定就是个0? 比如一般人在pc下写函数,正常返回非0,错误返回0,或者一个负的错误码,可是在VxWorks下,它就要返回个0来表示正确,你就不得不写成if( fn()==OK )而不能写if( fn() )。
所以为了能够很好的移植你的代码,同时为了安全,最好是用无隐含等价的语句,因此我倾向于用if(NULL==p) 或 if(is_nullptr(p)) 前者更可以在少打个=的时候得到编译错误消息,而后者在移植的时候则更加安全
[解决办法]
支持,但我个人认为用if( NULL == p)比较好,明确判断是不是空指针.而且很多教材也是这样判断指针的,觉得这样更通谷更易懂
[解决办法]
mark
xuexi;
[解决办法]
study