在C或C ++中,我应该检查针对NULL / nullptr的指针参数吗?
这个问题受到了这个答案的启发。
我始终认为,当调用者做了一些愚蠢的事情时,被调用者从不负责,比如传递无效参数。 我得出这个结论有几个原因,但也许最重要的一条来自这篇文章 :
没有定义的东西是未定义的。
如果一个函数没有在文档中说可以传递nullptr
,那么你最好不要传递nullptr
到那个函数。 我不认为这是被调查者的责任。
但是,我知道会有一些人不同意我的观点。 我很好奇我是否应该检查这些事情,为什么。
虽然一般来说,我没有看到在检测NULL(为什么NULL,而不是其他一些无效的地址?)的值,我可能仍然会这样做,只是因为许多C和C ++程序员期望这样的行为。
如果您要检查空指针参数,如果您没有input合约来接受和解释它们,请使用assert
而不是条件错误返回。 通过这种方式,调用者中的错误将被立即检测到,并且可以被修复,并且可以很容易地禁用生产构build中的开销。 我质疑assert
的价值,除了文件, 取消引用NULL指针的段错误对于debugging同样有效。
如果你把一个错误代码返回给一个已经被certificate是错误的调用者,最可能的结果就是调用者会忽略这个错误,而当错误的原因变得困难的时候,坏的事情将会在很晚的时候发生。不可能追查。 为什么假设调用者会忽略你返回的错误是合理的? 因为调用者已经忽略了malloc
或fopen
的错误返回或者其他一些返回NULL
来指示错误的库特定的分配函数!
在C ++中,如果你不想接受NULL指针,那么不要冒险 :接受一个引用。
“深度防御原则”说是。 如果这是一个外部的API,那么完全是必要的。 否则,至less有一个断言来协助debugging滥用你的API。
你可以logging合同,直到你脸色苍白,但你不能在被调用的代码中防止恶意或恶意滥用你的function。 你必须做出的决定是滥用的可能成本是多less。
在我看来,这不是一个责任问题。 这是一个健壮的问题。
除非我对调用者有完全的控制权, 而且我必须为了即使是微小的速度改进而优化,我总是检查NULL。
我的理念是:你的用户应该被允许犯错误,你的编程团队不应该这样做。
这意味着您应该检查无效参数(包括NULL)的唯一位置是在顶层用户界面中。 用户可以在任何地方向代码提供input,您应该检查错误并尽可能优雅地处理它们。
在其他地方,你应该使用ASSERTS来确保程序员正确地使用这些函数。
如果您正在编写一个API,那么只有顶级函数才能捕获并处理错误的input。 继续在你的调用堆栈中检查一个三到四层的NULL指针是没有意义的。
我严重依赖于“不要相信用户的input,不要放弃你的系统”和一般的防御性编程。 由于我已经在过去的生活API,我看到库的用户通过空指针,然后应用程序崩溃的结果。
如果它真的是一个内部的图书馆,而且我是唯一能够使用它的人(或者只有less数人),那么只要大家都同意遵守总体合同,我就可以放松空指针检查。 我不能相信整个用户群坚持这一点。
C和C ++的答案会有所不同。
C ++有参考。 传递指针和传递引用的唯一区别是指针可以为null。 所以,如果被调用的函数的作者期望一个指针参数,并忘记当它为空时做一些理智的事情,他是愚蠢的,疯狂的或写C类与类。
无论哪种方式,这不是一个谁戴责任帽的问题。 为了编写好的软件,这两个程序员必须合作, 所有程序员都有责任避免需要这种决定的特殊情况,并在这种情况下避免2°的错误,编写代码在非歧义和logging方式,以帮助debugging。
所以,当然,你可以指向和嘲笑调用者,因为他搞砸了,“没有定义的东西是不确定的”,不得不花一个小时debugging一个简单的空指针错误,但是你的团队浪费了一些宝贵的时间。
我是亲防守编程。
除非你能描述这些nullptr
检查发生在你的应用程序的瓶颈中(在这种情况下,可以想象的是,在这些点上不应该做那些指针值testing)
但是总的来说比较一个int
和0
是真的很便宜的一个操作。 我认为让潜在的崩溃错误而不是消耗如此之小的CPU是一种遗憾。
所以:testing你的指针对NULL!
我认为你应该努力编写对每一个可能的情况都是健壮的代码。 将NULL
指针传递给函数是非常普遍的; 因此,你的代码应该检查它并处理它,通常通过返回一个错误值。 库函数不应该使应用程序崩溃。
对于C ++,如果你的函数不接受空指针,那么使用一个引用参数。 一般来说。
有一些例外。 例如,包括我自己在内的很多人认为使用指针参数会更好,当实际的参数自然是一个指针时,特别是当函数存储指针副本的时候。 即使该函数不支持空指针参数。
对于无效论证要捍卫多less取决于主观舆论和直觉。
干杯&hth。,
有一件事你必须考虑如果一些调用者误用你的API会发生什么。 在传递NULL指针的情况下,结果是明显的崩溃,所以可以不检查。 调用代码的开发人员很容易发现任何滥用。
臭名昭着的glibc崩溃是完全另一回事。 滥用导致调用者实际上有用的行为,并且API停留了几十年。 然后他们改变了它。
在这种情况下,API开发人员应该使用断言或类似的机制来检查值。 但是你不能及时纠正错误。 嗷嗷待哺的牙齿是不可避免的。 在这里阅读所有关于它。
如果你不想要一个NULL,那么不要使这个参数成为一个指针。
通过使用引用,可以保证对象不会为NULL。
我不认为这是被调查者的责任
如果它不承担这个责任,它可能会创build错误的结果,如解除引用NULL
指针。 问题是它总是隐含地承担这个责任。 这就是为什么我喜欢优雅的处理。
在我看来,被执行者有责任执行合同。
如果被调用者不应该接受NULL
,那么它应该assert
。
否则,当被传递NULL
时,被调用者应该performance得很好 。 也就是说,根据您为其指定的合同,它应该在function上是无操作的,返回错误代码或分配自己的内存。 从呼叫者的angular度来看,它应该做任何看起来最明智的事情。
作为API的用户,我希望能够继续使用它,而不会让程序崩溃; 我希望能够至less恢复,或者最坏的情况下closures。
是的,你应该检查空指针。 你不想让应用程序崩溃,因为开发者搞砸了。
对无效或不存在的数据进行无效操作的人,只应该使其系统状态失效。
我认为这是完全废话,function,期望input应该检查NULL。 或者任何其他的价值。 一个函数的唯一工作就是根据它的input或范围状态来完成一个任务,没有别的。 如果您没有有效的input,或根本没有input,那么甚至不要调用该函数。 此外,NULL检查不会检测到其他数百万个可能的无效值。 你知道正手你会传递NULL,所以为什么你仍然会通过它,浪费宝贵的周期在另一个函数调用与parameter passing,在function上比较一些指针,然后再次检查函数输出是否成功。 当然,我可能在1982年6岁的时候就这样做了,但那些日子早已过去了。
有理由公开API。 就像一些提供白痴校验的DLL一样。 你知道,这些参数:“如果用户提供NULL,你不希望你的应用程序崩溃。” 多么无谓的争论 首先是通过伪造数据的用户; 这是一个明确的select,除此之外别无其他。 如果有人认为这是质量,那么…我喜欢坚实的逻辑和性能超过这种事情。 此外,程序员应该知道他在做什么。 如果他正在针对特定范围的无效数据进行操作,那么他没有业务称自己是程序员。 我认为没有理由降低性能,增加功耗,同时增加二进制大小,反过来影响我的产品的指令caching和分支预测,以支持这些用户。
在这种情况下,我称之为法律责任和道德责任是有区别的。 作为一个比喻,假设你看到一个视力不佳的人走向悬崖边缘,轻轻地不知道它的存在。 就你的法律责任而言,如果你不提出警告,他一般不可能成功起诉你,他会继续走路,从悬崖上掉下来,死亡。 另一方面,你有机会警告他 – 你有能力挽救他的生命,而你故意select不这样做。 普通人倾向于将这种行为视为蔑视,认为你有道义上的责任去做正确的事情。
这如何适用于手头的问题? 简单 – 被调用者对于调用者的行为,愚蠢或其他方式,例如传递无效input,不是“法律上”负责。 另一方面,当事情发生了变化,并且发现在你的function中进行简单的检查可以使呼叫者免于自己的愚蠢行为,你将最终分享一些发生事件的道德责任。
当然这里有一个权衡取决于检查实际花费你多less。 回想一下这个比喻,假设你发现同一个陌生人慢慢地朝着世界另一边的悬崖缓缓移动,并且用你的生命储蓄飞到那里并警告他,你可以救他。 如果在这种特殊的情况下,你忽略了这么做(假设电话还没有被发明出来,这个比喻),很less有人会严厉地判断你。 但是,在编码方面,如果检查和检查NULL
一样简单,即使你不这样做,你也是失职,即使情况中的“真正的”责任在于调用者。
这种方法的一个副作用是,当你的图书馆因为通过了一个无效的论证而崩溃时,你往往会受到指责。
没有比Windows操作系统更好的例子。 最初,微软的做法是消除许多虚假的testing。 结果是一个更高效的操作系统。
然而,事实是无效的论点一直都是通过的。 从程序员不能达到鼻烟,或者只是使用其他函数返回的值不希望为NULL。 现在,Windows执行更多的validation,结果效率更低。
如果你想允许你的例程崩溃,那么不要testing无效的参数。
开发时间和运行时间性能的开销与您正在devise的API的稳健性有一定的折衷。
如果您正在发布的API必须在调用例程的进程中运行,那么您不应该检查NULL或无效的参数。 在这种情况下,如果你崩溃,客户端程序崩溃,开发人员使用你的API应该修补他的方式。
但是,如果你正在提供一个运行时/框架来运行其中的客户端程序(例如,你正在编写一个虚拟机或者一个可以托pipe代码或者操作系统的中间件),那么你应该检查它的正确性争论通过。 你不希望你的程序被指责为一个插件的错误。