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

时间:2010-12-08 16:49:34

标签: c++ c null

这个问题的灵感来自this answer

我一直认为,当调用者做一些愚蠢的事情时,被调用者永远不会负责,比如传递无效参数。我得出这个结论有几个原因,但也许最重要的一个来自this article

  

未定义的所有内容都是未定义的。

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

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

20 个答案:

答案 0 :(得分:42)

如果您要检查没有输入合同以接受和解释它们的NULL指针参数,请使用assert,而不是条件错误返回。这样,调用者中的错误将立即被检测到并且可以修复,并且可以轻松地禁用生成构建中的开销。我质疑assert的价值,但文件除外;解除引用NULL指针的段错误对调试同样有效。

如果您将错误代码返回给已经证明自己有错误的来电者,最可能的结果是调用者会忽略错误,而错误的事情会在以后发生。错误的原因很难或无法追查。为什么假设调用者会忽略您返回的错误是合理的?因为调用者已经忽略了mallocfopen或其他一些特定于库的分配函数的错误返回,该函数返回NULL以指示错误!

答案 1 :(得分:28)

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

答案 2 :(得分:14)

虽然我没有看到公共API检测NULL(为什么是NULL而不是其他一些无效地址?)的值,但我可能仍然只是因为许多C和C ++程序员都期望这样的行为。 / p>

答案 3 :(得分:8)

深度防御原则是肯定的。如果这是一个外部API,那么完全必不可少。否则,至少有一个断言来协助调试API的滥用。

您可以记录合同,直到您脸红,但您不能使用被叫方代码来防止对您的功能进行不明智或恶意的滥用。您必须做出的决定是滥用的可能成本。

答案 4 :(得分:6)

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

除非我对调用者有完全的控制权,否则我必须优化甚至提高分钟速度,我总是检查是否为NULL。

答案 5 :(得分:4)

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

这意味着您应该检查包括NULL在内的无效参数的唯一位置是在顶级用户界面中。用户可以在任何地方为代码提供输入,您应该检查错误,并尽可能优雅地处理它们。

在其他地方,您应该使用ASSERTS来确保程序员正确使用这些功能。

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

答案 6 :(得分:4)

我非常倾向于不要相信用户的输入,以免炸毁你的系统'一般来说,在防御性编程中。由于我在过去的生活中已经创建了API,因此我看到库的用户传入空指针然后导致应用程序崩溃。

如果它真的是一个内部库,我唯一的人(或只有少数几个人)有能力使用它,那么只要每个人都同意遵守,我就可以放松空指针检查一般合同。我无法信任整个用户群,以坚持这一点。

答案 7 :(得分:4)

对于C和C ++,答案会有所不同。

C ++有参考。传递指针和传递引用之间的唯一区别是指针可以为null。因此,如果被调用函数的编写者需要一个指针参数并且在它为空时忘记做一些理智的事情,那么他就是愚蠢,疯狂或写C-with-classes。

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

所以,当然,你可以指向并嘲笑调用者因为他搞砸了并且“未定义的所有内容都是未定义的”并且不得不花费一个小时来调试一个简单的空指针错误,但是你的< em>团队浪费了一些宝贵的时间。

答案 8 :(得分:3)

我是专业的防御性编程。

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

但总而言之,将int0进行比较实际上是便宜一项操作。 我认为让潜在的崩溃错误而不是消耗如此少的CPU是一种耻辱。

所以:测试指针对NULL!

答案 9 :(得分:1)

您必须考虑的一件事是,如果某些来电者滥用您的API会发生什么。在传递NULL指针的情况下,结果是明显的崩溃,因此可以不检查。对于调用代码的开发人员来说,任何滥用都是显而易见的。

臭名昭着的glibc崩溃完全是另一回事。滥用导致调用者实际上有用的行为,并且API保持这种状态数十年。然后他们改变了它。

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

答案 10 :(得分:1)

对无效或不存在的数据执行无效操作的人,只能使他的系统状态失效。

我认为完全无意义的是,期望输入的函数应该检查NULL。或者其他任何有价值的东西。函数的唯一作用是根据其输入或范围状态执行任务,而不是其他任务。如果您没有有效输入或根本没有输入,那么甚至不要调用该功能。此外,NULL检查不会检测其他数百万个可能的无效值。你知道你正在传递NULL,所以为什么你仍然会传递它,浪费宝贵的循环在另一个函数调用参数传递,一些指针的函数内比较,然后再次检查函数输出是否成功。当然,我在1982年6岁的时候可能会这样做,但那些日子早已过去了。

对于公共API,我们可以提出这个论点。像一些DLL提供白痴检查。您知道,这些论点:&#34;如果用户提供NULL,您不希望您的应用程序崩溃。&#34;什么是非争论。用户首先传递虚假数据;它是一个明确的选择,除此之外别无他法。如果一个人觉得质量好,那么......我更喜欢坚实的逻辑和表现而不是这些事情。此外,程序员应该知道他在做什么。如果他针对特定范围操作无效数据,那么他就没有业务称自己为程序员。我认为没有理由降低性能,增加功耗,同时增加二进制大小,进而影响我的产品的指令缓存和分支预测,以支持这些用户。

答案 11 :(得分:1)

如果您不想要NULL,则不要将参数设为指针 通过使用引用,您可以保证对象不会为NULL。

答案 12 :(得分:1)

对于C ++,如果您的函数不接受nullpointer,则使用引用参数。总的来说。

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

防止无效论证取决于多少,包括它取决于主观意见和直觉。

干杯&amp;第h。,

答案 13 :(得分:1)

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

答案 14 :(得分:0)

在我看来,被叫方有责任执行合同。

如果被叫方不接受NULL,那么它应该assert

否则,当被传递给NULL时,被叫方应该表现良好。也就是说,它应该在功能上是无操作,返回错误代码,或分配自己的内存,具体取决于您为其指定的合同。它应该从呼叫者的角度做任何看似最合理的事情。

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

答案 15 :(得分:0)

这种方法的一个副作用是,当你的库因为传递了无效的参数而崩溃时,你会倾向于受到指责。

没有比Windows操作系统更好的例子了。最初,微软的方法是消除许多伪造论证的测试。结果是操作系统更有效率。

然而,现实是无效的参数一直传递。从没有使用snuff的程序员,或者只是使用其他函数返回的值,预计不会是NULL。现在,Windows执行更多验证,因此效率较低。

如果您想让例程崩溃,请不要测试无效参数。

答案 16 :(得分:0)

是的,你应该检查空指针。您不希望崩溃应用程序,因为开发人员搞砸了。

答案 17 :(得分:0)

  

我不认为被叫方有责任处理这类事情

如果它不承担此责任,则可能会产生错误的结果,例如解除引用NULL指针。问题是它总是隐含地承担这个责任。这就是为什么我更喜欢优雅的处理。

答案 18 :(得分:-1)

在这种情况下,我所谓的法律和道德责任之间存在着区别。作为一个类比,假设你看到一个视力不好的人走向悬崖边缘,一点也不知道它的存在。就你的法律责任而言,如果你没有警告他并且他继续走路,从悬崖上坠落并死亡,那么通常不可能成功地起诉你。另一方面,你有机会警告他 - 你有能力挽救他的生命,你刻意选择不这样做。一般人倾向于蔑视这种行为,判断你有一个道德责任做正确的事。

这如何适用于手头的问题?简单 - 被调用者不会“合法地”对调用者的操作负责,愚蠢或其他方式,例如传入无效输入。另一方面,当事情发生变化时,观察到你的功能中的简单检查可以使调用者免于自己的愚蠢行为,你最终将分担一些道德责任。

当然,这里需要进行权衡取决于支票实际花费多少。回到类比,假设你发现同一个陌生人慢慢地朝着世界另一边的悬崖慢慢地移动,并且通过花费你的生命积蓄飞到那里并警告他,你可以救他。如果在这种特殊情况下你忽视了这一点,那么很少有人会完全严厉地评判你(让我们假设电话没有被发明,为了这个比喻的目的)。但是,在编码方面,如果检查就像检查NULL一样简单,如果你没有这样做,你就会失职,即使情况中的“真实”责任在于调用者。

答案 19 :(得分:-1)

开发时间+运行时性能的开销与您正在设计的API的稳健性进行权衡。

如果要发布的API必须在调用例程的进程内运行,则不应该检查NULL或无效参数。在这种情况下,如果您崩溃,客户端程序崩溃,使用您的API的开发人员应该修复他的方式。

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