在C或C ++中,我应该针对NULL / nullptr检查指针参数吗?

In either C or C++, should I check pointer parameters against NULL/nullptr?

这个问题受到这个答案的启发。

我一直坚持这样的理念:当调用者做了一些愚蠢的事情,比如传递无效参数时,被调用者从不负责。我得出这个结论有几个原因,但最重要的一个原因可能来自本文:

Everything not defined is undefined.

如果一个函数在它的文档中没有说明传递nullptr是有效的,那么最好不要将nullptr传递给该函数。我不认为被叫方有责任处理这些事情。

不过,我知道会有一些人不同意我的看法。我很好奇我是否应该检查这些东西,为什么。


如果要检查没有签订接受和解释合同的空指针参数,请使用assert,而不是条件错误返回。这样,调用程序中的错误将被立即检测出来并可以被修复,并且可以很容易地禁用生产构建中的开销。我对EDOCX1的值(0)表示怀疑,但文档除外;取消引用空指针的segfault对于调试同样有效。

如果您将一个错误代码返回给一个已经证明自己有问题的调用者,最可能的结果是调用者将忽略该错误,并且当错误的原始原因变得难以或不可能被跟踪时,糟糕的事情将在稍后发生。为什么合理地假设调用者会忽略您返回的错误?因为调用者已经忽略了mallocfopen的错误返回,或者其他一些特定于库的分配函数返回NULL来指示错误!


在C++中,如果你不想接受空指针,那么不要冒险:接受一个引用。


一般来说,我在检测空值时看不到该值(为什么为空,而不是其他一些无效地址?)对于一个公共API,我可能仍然会这么做,因为很多C和C++程序员都希望这样的行为。


纵深防御原则是肯定的。如果这是一个外部API,那么这是完全必要的。否则,至少要有一个断言来帮助调试对API的误用。

您可以记录合同,直到您的脸是蓝色的,但您不能在被调用代码防止不明智或恶意滥用您的功能。你必须做出的决定是什么是可能的误用成本。


在我看来,这不是责任问题。这是一个稳健性的问题。

除非我对调用者有完全的控制权,而且我必须优化,即使是分钟的速度提高,我总是检查是否为空。


我的理念是:你的用户应该被允许犯错误,你的编程团队不应该。

这意味着,您应该检查的唯一无效参数(包括空值)是在顶级用户界面中。无论用户在哪里可以为代码提供输入,您都应该检查错误,并尽可能优雅地处理它们。

在其他地方,您应该使用断言来确保程序员正确地使用函数。

如果您正在编写API,那么只有顶级函数才应该捕获并处理错误的输入。在调用堆栈的三层或四层中继续检查空指针是没有意义的。


我非常倾向于"不要相信你的用户的输入不会破坏你的系统"和一般的防御性编程。因为我在过去的生活中制造了API,所以我看到库的用户传递了空指针,然后导致应用程序崩溃。

如果它真的是一个内部库,而我是唯一(或者只有少数人)有能力使用它,那么只要每个人都同意遵守一般合同,我就可以轻松地进行空指针检查。我不能相信广大用户会坚持这一点。


答案将是不同的C和C++。

C++有参考文献。传递指针和传递引用之间的唯一区别是指针可以为空。所以,如果被调用函数的编写者期望一个指针参数,当它为空时忘记做一些明智的事情,那么他是愚蠢的、疯狂的或是用类编写C语言。

不管怎样,这不是谁戴责任帽的问题。为了编写好的软件,两个程序员必须合作,所有程序员的责任是1°避免需要这种决定的特殊情况,2°当失败时,编写以非模糊和文档化方式爆炸的代码,以帮助调试。

所以,当然,您可以指向调用者并嘲笑他,因为他搞砸了,"所有未定义的东西都是未定义的",并且必须花一个小时调试一个简单的空指针错误,但是您的团队在这方面浪费了一些宝贵的时间。


我支持防御编程。

除非您能够分析这些nullptr检查发生在应用程序的瓶颈中…(在这种情况下,可以想象,不应该在这些点上进行这些指针值测试)

但总的来说,将int0进行比较,操作起来确实很便宜。我认为让潜在的崩溃bug而不是消耗这么少的CPU是一种耻辱。

所以:测试你的指针是否为空!


我认为您应该努力编写对于每种可能的情况都是健壮的代码。向函数传递NULL指针是非常常见的;因此,您的代码应该检查并处理它,通常通过返回一个错误值。库函数不应使应用程序崩溃。


您必须考虑的一件事是,如果某个调用者误用了您的API,会发生什么情况。在传递空指针的情况下,结果是一个明显的崩溃,因此可以不检查。调用代码的开发人员很容易发现任何误用。

臭名昭著的葛里布崩溃完全是另一回事。这种误用为调用者带来了实际的有用行为,而API在这方面保持了几十年。然后他们改变了。

在这种情况下,API开发人员应该使用断言或类似机制检查值。但你不能及时回去改正错误。哀号和咬牙切齿是不可避免的。在这里阅读所有相关内容。


对于C++,如果函数不接受null指针,则使用引用参数。一般来说。

也有一些例外。例如,许多人,包括我自己,都认为当实际参数自然是指针时,使用指针参数更好,特别是当函数存储指针副本时。即使函数不支持nullpointer参数。

反对无效论点的程度取决于,包括它取决于主观观点和直觉。

干杯!


对无效或不存在的数据执行无效操作的人,只应使其系统状态变为无效。

我认为期望输入的函数检查是否为空完全是胡说八道。或者其他的价值。函数的唯一任务是根据其输入或作用域状态执行任务,而不是其他任务。如果没有有效的输入,或者根本没有输入,那么甚至不要调用函数。此外,空检查不会检测到其他数百万个可能的无效值。你知道,正手传递的是空值,所以为什么还要传递它,在另一个传递参数的函数调用上浪费宝贵的循环,在函数中比较一些指针,然后再次检查函数输出是否成功。当然,1982年我6岁的时候就已经这样做了,但是那些日子已经过去很久了。

当然,对于公共API也有一些争论,比如一些提供了防白痴检查的DLL。你知道,这些参数:"如果用户提供了空值,你就不希望你的应用程序崩溃。"真是一个非参数。首先传递虚假数据的是用户;这是一个明确的选择,除此之外别无选择。如果有人觉得这是质量,那么…比起这些事情,我更喜欢可靠的逻辑和性能。另外,程序员应该知道他在做什么。如果他对特定范围内的无效数据进行操作,那么他就没有必要称自己为程序员。我认为没有理由降低性能,增加功耗,同时增加二进制文件的大小,这反过来影响我的产品的指令缓存和分支预测,以支持这样的用户。


如果不需要空值,则不要使参数成为指针。通过使用引用,可以保证对象不会为空。


这种方法的一个副作用是,当您的库因传递无效参数而崩溃时,您往往会受到责备。

没有比Windows操作系统更好的例子了。最初,微软的方法是消除许多虚假论点的测试。结果是一个更高效的操作系统。

然而,事实是无效的参数总是被传递。对于不符合标准的程序员,或者只是使用其他函数返回的值,不应该是空的。现在,Windows执行更多的验证,因此效率较低。

如果你想让你的程序崩溃,那么不要测试无效的参数。


在我看来,被呼叫者有责任履行合同。

如果被叫方不接受NULL,则应接受assert

否则,当被呼叫者被交给NULL时,应该表现得很好。也就是说,它在功能上应该是一个no-op,返回一个错误代码,或者分配它自己的内存,这取决于您为它指定的契约。从呼叫者的角度来看,它应该做任何看起来最明智的事情。

作为API的用户,我希望能够在不发生程序崩溃的情况下继续使用它;我希望至少能够恢复或者在最坏的情况下优雅地关闭它。


I don't think it's the responsibility of the callee to deal with such things

如果它不承担这一责任,可能会产生不良的结果,比如取消引用NULL指针。问题是它总是含蓄地承担这个责任。这就是为什么我更喜欢优雅的操控。


是的,您应该检查空指针。你不想让一个应用程序崩溃,因为开发人员把事情搞砸了。


开发时间+运行时性能的开销与您正在设计的API的健壮性有一个权衡。

如果要发布的API必须在调用例程的进程内运行,则不应检查是否存在空参数或无效参数。在这种情况下,如果崩溃,客户机程序就会崩溃,使用API的开发人员应该改进他的方法。

但是,如果您提供的运行时/框架将在其中运行客户端程序(例如,您正在编写虚拟机或可承载代码或操作系统的中间件),则必须检查传递的参数的正确性。你不希望你的程序因插件的错误而受到指责。


在这种情况下,法律责任和道德责任是有区别的。作为一个类比,假设你看到一个视力不好的人朝悬崖边走去,却不知道它的存在。就你的法律责任而言,如果你没有警告他,而他继续走着,从悬崖上摔下来,然后死去,那么一般情况下就不可能成功地起诉你。另一方面,你有机会警告他——你可以拯救他的生命,你故意选择不这样做。一般人会轻视这种行为,认为你有道德责任去做正确的事情。

这如何适用于手头的问题?简单——被调用者对调用者的行为不负有"法律上的"责任,不管是愚蠢的还是其他的,比如传入无效的输入。另一方面,当事情发展到崩溃的时候,你的功能中的一个简单的检查可以让调用者从自己的愚蠢中解脱出来,你最终会为所发生的事情分担一些道德责任。

当然,这是一种权衡,取决于支票的实际成本。回到这个类比,假设你发现这个陌生人正缓慢地向世界另一边的一个悬崖缓缓移动,并且通过花你的毕生积蓄飞去警告他,你可以救他。在这种情况下,如果你忽视了这一点,很少有人会对你做出完全严厉的评价(假设电话并不是为了这个类比而发明的)。然而,在编码方面,如果检查和检查NULL一样简单,那么如果您不这样做,即使这种情况下的"真正的"责备是由调用者造成的,您也可以放心。