这个问题受到这个答案的启发。
我一直坚持这样的理念:当调用者做了一些愚蠢的事情,比如传递无效参数时,被调用者从不负责。我得出这个结论有几个原因,但最重要的一个原因可能来自本文:
Everything not defined is undefined.
如果一个函数在它的文档中没有说明传递nullptr是有效的,那么最好不要将nullptr传递给该函数。我不认为被叫方有责任处理这些事情。
不过,我知道会有一些人不同意我的看法。我很好奇我是否应该检查这些东西,为什么。
- 这个问题似乎与最近的glibc memcpy崩溃(bugzilla.redhat.com/show_bug.cgi)有关。ID=638477)。
- 我同意你120%的观点。
- @ R.。-我同情这个观点,但在任何情况下都不同意。如果您正在编程一个新的win32 API呢?你只需要做得比"对不起,你是索尔"更好,在明显的无效输入上,比如空的原始指针。
- 你为什么要做得更好?你建议的"更好"是什么?
- @丽姬:哇,多蠢的线啊。为什么没有人提出正确的解决方案来修补Buggy专有软件的重新定位表,使其像应该的那样简单地调用memmove?
- 天啊,我没想到这会变成一场宗教战争。
- 之所以成为一场宗教战争,部分原因是我们中的一些人一直在写性能关键型函数,这些函数花费75%的时间检查所有参数是否为空,其余的25%执行一个简单的操作,这是由于遗留的API要求和/或需要特殊解释空值的中断调用程序。因此,我强烈反对任何在库用户中延续"传递空值是可以的"这一神话的做法,除非函数有很好的理由接受空值,并且将其记录为这样。
- "当调用者做了一些愚蠢的事情时,被调用者从不负责",这是完全正确的,但是您的代码实际负责的和您的代码实际被指责的之间有很大的区别。
- 请注意,我描述的情况在最初设计API时并不是很明显,因为函数最初有一个更复杂的任务,当您可以删除对遗留垃圾的支持时,这个任务只会变得微不足道。想象一下,大型机软件中的EBCDIC到ASCII转换功能被移植到现代机器上。您模拟了转换,现在所有的函数都在浪费时间将指针与空值进行比较,但是您不能只将cx1〔1〕转换为no op,因为调用方希望进行空检查…
- 病人对医生说:"当我抬起胳膊的时候会疼。"医生回答说"那就不要这样做"。
- @好吧,病人太笨了,不知道这一点,所以你希望打电话的人知道他们所做的是"愚蠢的"?
- @R:有人确实提出了这个建议(修补bugy flash插件的重定位表)。
- @我不明白你的意思。
- API用户对API开发人员说:"当我传递无效参数时,它崩溃了。"开发人员回答"那就不要这样做"。--通常最好是治疗病因而不是症状。
- @在您的案例中,崩溃是一个症状,传递的错误参数是原因。与医生的情况正好相反。
如果要检查没有签订接受和解释合同的空指针参数,请使用assert,而不是条件错误返回。这样,调用程序中的错误将被立即检测出来并可以被修复,并且可以很容易地禁用生产构建中的开销。我对EDOCX1的值(0)表示怀疑,但文档除外;取消引用空指针的segfault对于调试同样有效。
如果您将一个错误代码返回给一个已经证明自己有问题的调用者,最可能的结果是调用者将忽略该错误,并且当错误的原始原因变得难以或不可能被跟踪时,糟糕的事情将在稍后发生。为什么合理地假设调用者会忽略您返回的错误?因为调用者已经忽略了malloc或fopen的错误返回,或者其他一些特定于库的分配函数返回NULL来指示错误!
- 放置assert()但没有条件的return的缺点是:如果您没有测试所有可能的代码执行方法(在指针可能危险为nullptr的所有精确位置),那么可能会发生应用程序崩溃。有条件返回允许应用程序在没有崩溃的情况下继续工作。
- 在一个真正反常的"工作"的意义上。这大致相当于为SIGSEGV和longjmp的输出设置一个信号处理器。调用者已经证明它有主要的错误,并且不能保证它的数据还没有见鬼。
- 不,绝对不是。拥有指向空值的指针本身并不总是错误。如果你不得不取消一个指针的引用,我认为它应该被测试。逻辑上。你可以返回错误代码,然后有一个体面的行为。
- 拥有空指针不是错误。将它传递给文档中要求数据指针的函数是一个错误。我已经解释了为什么返回错误可能被忽略。
- 这个答案几乎被选中了(它确实得到了+1;)。但是,我勾选了另一个答案,仅仅是因为它的观点与我在原始问题中提出的观点相反。
- @StephaneRolland我认为最安全的方法是同时执行这两种操作,即if (!input) { assert(false); return; }。这样,程序意图就可以用assert()来记录,我们可以很好地处理NDEBUG的定义和assert()宏没有效果的情况。
- @杰米布洛克:这不是个好主意。错误使用assert隐藏了调试时的问题(assert无法打印实际的断言,input != NULL将立即显示错误是什么),如果return;成功地使其成为未启用断言的生产构建,则它隐藏了错误。您想在这里崩溃,或者通过assert或者空指针取消引用。
在C++中,如果你不想接受空指针,那么不要冒险:接受一个引用。
- 如果可能的话,我会这样做。不过,有时我需要公开一个C API,这会让我处理指针:)
- 同意。C++中指针的语义是它可以是空的。如果不能为空,则使用引用。您没有,因此允许空值。
- 我不同意。通过C++中的引用隐藏了被调用方可能修改数据的事实,当它们被用于编写时,应该被认为是有害的。传递指针绝不意味着空值是有效的。此外,在实践中,引用可以是空的,例如function_that_takes_reference(*ptr);,其中ptr是NULL。(不要引用无用的形式规范;实际情况是函数中的机器代码将看到一个空指针,并将取消对它的引用。)
- @R.,"实际情况是,函数中的机器代码将看到一个空指针,并将取消对它的引用。"—不;取消引用发生在调用函数之前,并且是未定义的行为。指出未定义行为的典型例子是,事实上,未定义行为离"引用一个无用的形式规范"有着我所能想象的距离。至于"隐瞒被呼叫者可能修改数据的事实",我已经在互联网上的其他地方讨论了很多页,但仍然发现这个观点充其量是可疑的。
- 不管怎样,从调试的角度来看,崩溃都将发生在库函数内部,而不是调用程序中。我不关心C++规范试图假装引用不仅仅是指针的语法糖,实际上它们是相同的东西。
- @r.:好吧,如果有一个空引用,那么您已经有了未定义的行为,因为要分配引用,您必须取消对空指针的引用。
- @R.,这是完全不正确的;在许多情况下,编译器可以完全优化引用,但不能使用指针间接优化"等效"代码。至于崩溃发生在哪里,这与谁编写了坏代码的问题无关。你不能用"但是崩溃是在别人的代码运行之前才发生"来合理地保护未定义的行为,就像你不能用"但是错误在编译器试图编译它之前并不明显"来合理地保护语法错误一样。
- 或者换句话说:"我不关心C++规范试图假装一个数组的末尾内存不存在;如果我写了一个数组的边界并损坏堆,崩溃会在你的DELIST语句中发生。"
- 唯一不能让中断调用程序调用未定义行为的代码是代码,其中每个参数的位组合都有一个有效的解释。如果调用者愿意,任何使用指针的东西(不管你叫什么名字)都可以用来调用ub。形式上,通过C++引用,调用方通过传递错误指针来调用UB,但是在实际效果方面,这是相同的。不管您使用的是引用还是指针,如果一个无效的地址到达函数,这是调用方的错误。
- "唯一不能被中断的调用程序调用未定义行为的代码"…你好像还是不明白。在"空引用"示例中,调用方调用的是未定义的行为,而不是被调用的函数。只有当被调用的函数(a)不相关,(b)甚至不总是发生什么时,才检测到未定义的行为,这是由于未定义的行为的性质。"…如果一个无效的地址到达了函数,那就是调用方的错误。"这正是关键所在!不应将此归咎于被调用的函数。
- @Karl Knechtel:因此,如果您不想在函数中处理空指针,那么解决方案就是简单地定义"用空指针调用这个函数是未定义的行为",并且立即将责任转移给调用方,不管崩溃发生在何处。
- @CAF问题是,当标准规定某些行为是不确定的,而"某物"对语言是通用的时,人们往往会比你要求某个特定的应用是不确定的行为时更认真一点,因为这对你很方便。:)
- @Karl Knechtel:问题是你必须允许你的图书馆作者奢侈地定义"未定义的行为",否则你可以传递一个已经释放的指向函数的指针,然后声称任何随后的崩溃都是被调用函数的错误。
- 在C++中获得空引用是很容易的。Memset做得很好。
- 这个答案意味着,通过将空值的取消引用从被调用方内部移动到外部,某种程度上避免了一些"机会"。当然,没有什么是真的……无论哪种方式,都有ub,实际上它是完全相同的——生成相同的代码,并且取消对空值的引用的机器指令出现在被调用方内部。@ R.。和caf一样,把函数的约定定义为不允许空指针就足够了。一个不认真对待这个问题的程序员是不称职的。
一般来说,我在检测空值时看不到该值(为什么为空,而不是其他一些无效地址?)对于一个公共API,我可能仍然会这么做,因为很多C和C++程序员都希望这样的行为。
- +1对于这里为数不多的给出原因的答案之一,而不仅仅是"好吧,这就是我所做的…"
- 事实上,这是我见过的唯一一个理智的专业检查答案…
- @比利和@r:是的,我不想去职业检查营:(
- 选中此答案是因为1。作者的回答与什么是一个相当加载的问题相反,反对进行空检查,和2。事实上,他们的回答是有理由的,而不仅仅是"好吧,这是我的观点"。
纵深防御原则是肯定的。如果这是一个外部API,那么这是完全必要的。否则,至少要有一个断言来帮助调试对API的误用。
您可以记录合同,直到您的脸是蓝色的,但您不能在被调用代码防止不明智或恶意滥用您的功能。你必须做出的决定是什么是可能的误用成本。
- 一般来说,您不能检查无效参数。仅检查空值可以防止极少量的错误。我的主张是,因为库一般不能防止对函数的滥用,而且函数无论如何都会在这种错误上崩溃,所以最好在错误所在处引起崩溃,而不是将其转换为可以忽略的错误代码。+但是对于断言建议是1。
- 我同意"无法处理一般情况"的评论,但我认为如果允许原始指针,还是值得检查NULL。不管怎样,使用无效指针都是ub,因此如果您想在无效输入上"导致崩溃",在一般情况下也不可能这样做。你能做的最好的事情是像IsBadReadPtr这样的东西(在Windows上),这就是疯狂——开发人员找不到他们的bug的最后手段。
- @史蒂夫:isbadxxxptr真的应该叫做crashprogramrandomly。这就是我问这个问题的另一个原因:)
- @比利-对,或者是埃多克斯1〔2〕。
- +1用于assert();IMO不值得在发布代码中使用运行时命中来防止空指针。检查空值并不总是能保护您的安全,例如,即使API添加了大量空指针检查,我仍然可以通过向一个期望能够修改内存的函数传递一个指向字符串文字的指针来导致未定义的行为。
- @祈祷者:或者有人可能刚好通过&ptr_that_happens_to_be_null[1]……-)
在我看来,这不是责任问题。这是一个稳健性的问题。
除非我对调用者有完全的控制权,而且我必须优化,即使是分钟的速度提高,我总是检查是否为空。
- 定义"稳健性",请:)
- 在这种情况下,健壮性意味着最小化"核心转储"或"访问冲突"的机会,而不是更优雅地处理调用方错误。
- 如果最小化核心转储的机会意味着增加用损坏的废话覆盖磁盘上文件的机会,该怎么办?
- @ R.。你提出了一个很好的观点。不同的情况需要以不同的方式处理。它真的取决于正在编写的库及其可预见的用途。用同样的方法,人们会问"如果不检查空值,会怎样增加用损坏的废话覆盖磁盘上文件的可能性?"作为被呼叫者,你能为呼叫者做的只有这么多。还要考虑调试和开发时间的价值:作为一个程序员,我更喜欢从库函数接收一个文档化的错误返回代码,而不是查找核心转储。
- 如果您一致地检查返回值是否有错误,那么您几乎肯定不会首先向库函数传递空指针。
我的理念是:你的用户应该被允许犯错误,你的编程团队不应该。
这意味着,您应该检查的唯一无效参数(包括空值)是在顶级用户界面中。无论用户在哪里可以为代码提供输入,您都应该检查错误,并尽可能优雅地处理它们。
在其他地方,您应该使用断言来确保程序员正确地使用函数。
如果您正在编写API,那么只有顶级函数才应该捕获并处理错误的输入。在调用堆栈的三层或四层中继续检查空指针是没有意义的。
我非常倾向于"不要相信你的用户的输入不会破坏你的系统"和一般的防御性编程。因为我在过去的生活中制造了API,所以我看到库的用户传递了空指针,然后导致应用程序崩溃。
如果它真的是一个内部库,而我是唯一(或者只有少数人)有能力使用它,那么只要每个人都同意遵守一般合同,我就可以轻松地进行空指针检查。我不能相信广大用户会坚持这一点。
- 我的断言是"应用程序崩溃"是这里需要的行为。
- 这是一种邪恶的欲望;—)
- @史蒂芬:是的。但崩溃总比在不一致的状态下继续执行要好,这可能会破坏其他数据。除非你在控制核反应堆,否则最好是惨败,而不是默默地失败,如果你必须失败。
- @比利-如果是这样的话,我希望在终止应用程序时能处理得稍微优雅一点,比如通过一些日志记录,因为我们的用户并不擅长调试,而且主要依靠我们的开发团队来解决这样的错误(不幸的是,我们没有发送PDB来帮助他们调试)。我确实看到了你的观点,尤其是在一般情况下,你不会通过NULL,而是只会得到一个指向垃圾的指针。真的无法抵御。
- 在错误上崩溃听起来比没有发现你有严重的缺陷要好,但是它崩溃的方式是否暗示了错误的原因?如果堆栈跟踪中充满了库函数的实现细节,则很难追溯到调用方的错误。
- @克里斯:即使您的堆栈跟踪不包含库函数,它也肯定包含您最后一次调用库的过程。
- 不需要调试程序来测试这一点。静态分析可以告诉程序员在分配指针时忘记检查错误返回。
- @比利,嘿,我们不是在处理无效的指针。我们正在处理空指针。我认为问题完全不同。
- 空指针是无效指针。遗憾的是,很多C和C++新手并不理解这一点。
- @billyoneal——通常是的,但是触发堆栈跟踪的错误并不总是与调用中的错误相链接。这可能是由于前一个电话设置的定时炸弹,并由一个显然有效的后续电话触发。它可以通过不明显的实现逻辑与当前调用的参数进行远程关联。在一些不幸的情况下,堆栈跟踪输出可能会在返回到一个函数之前被截断,而这个函数正是试图解决问题的人所拥有的。
- @r当然nullptr是一个无效的指针,我理解您的精度。然而,我在一个空值指针和一个无效值指针之间做了一个精确的区分(例如一个随机整数,当然不同于0/空)。我总是清晰地初始化所有指向空值的指针,并且我确信始终初始化其变量是一种干净的行为:因此始终具有空值的somewhere指针是一种干净而正常的行为。
答案将是不同的C和C++。
C++有参考文献。传递指针和传递引用之间的唯一区别是指针可以为空。所以,如果被调用函数的编写者期望一个指针参数,当它为空时忘记做一些明智的事情,那么他是愚蠢的、疯狂的或是用类编写C语言。
不管怎样,这不是谁戴责任帽的问题。为了编写好的软件,两个程序员必须合作,所有程序员的责任是1°避免需要这种决定的特殊情况,2°当失败时,编写以非模糊和文档化方式爆炸的代码,以帮助调试。
所以,当然,您可以指向调用者并嘲笑他,因为他搞砸了,"所有未定义的东西都是未定义的",并且必须花一个小时调试一个简单的空指针错误,但是您的团队在这方面浪费了一些宝贵的时间。
我支持防御编程。
除非您能够分析这些nullptr检查发生在应用程序的瓶颈中…(在这种情况下,可以想象,不应该在这些点上进行这些指针值测试)
但总的来说,将int与0进行比较,操作起来确实很便宜。我认为让潜在的崩溃bug而不是消耗这么少的CPU是一种耻辱。
所以:测试你的指针是否为空!
- 我不在乎执行速度。另一方面,我确实关心的是必须不断地编写样板代码和文档。
- 但正如您所说,您编写的函数将引用作为输入,而不是指针。所以你不在乎这么小的锅炉板代码!是吗?-)
- 参考如何产生样板?所以我用&代替*。
- @比尔,我只是在开玩笑,如果你用了很多参考,你就不会用那么多的指针,所以没有那么多的指针检查…没有什么特别严重的事:—)
我认为您应该努力编写对于每种可能的情况都是健壮的代码。向函数传递NULL指针是非常常见的;因此,您的代码应该检查并处理它,通常通过返回一个错误值。库函数不应使应用程序崩溃。
- -1个给草人。如果程序将无效指针传递到库,而程序因此崩溃,则这不是使应用程序崩溃的库函数。""库函数不应该使应用程序崩溃"是一个非常重要的原则,但在这里不适用。如果图书馆打电话给malloc,但没有检查结果,这将适用。
- 但库函数并没有使应用程序崩溃;调用程序崩溃了。对于库来说,没有一般的方法来确定给定的内存缓冲区是否有效。
- @比利:在很多系统上,比如Linux上都有。在每次访问之前,请致电write(devnullfd, ptr, sizeof(*ptr));并检查EFAULT。我认为我们每个库函数都会增加50000个周期的write开销,以确保没有白痴使用无效指针调用我们的函数!!!!
- @R.从根本上讲,这和我们已经规定的isvalidxxxptr是一个问题。
- @比利:我希望你意识到这个评论是讽刺的…
- @我觉得第一部分很严肃,第二部分很讽刺。NVM然后:P
您必须考虑的一件事是,如果某个调用者误用了您的API,会发生什么情况。在传递空指针的情况下,结果是一个明显的崩溃,因此可以不检查。调用代码的开发人员很容易发现任何误用。
臭名昭著的葛里布崩溃完全是另一回事。这种误用为调用者带来了实际的有用行为,而API在这方面保持了几十年。然后他们改变了。
在这种情况下,API开发人员应该使用断言或类似机制检查值。但你不能及时回去改正错误。哀号和咬牙切齿是不可避免的。在这里阅读所有相关内容。
- 对不起,我对油嘴滑舌的事不太忠诚。(这是一些评论人写的)
- 它提供了一个很好的对比,在您的问题和另一个类似的案件与微妙的不同情况。请参阅lwn.net/articles/414467——这是一本有趣的书,我推荐。
对于C++,如果函数不接受null指针,则使用引用参数。一般来说。
也有一些例外。例如,许多人,包括我自己,都认为当实际参数自然是指针时,使用指针参数更好,特别是当函数存储指针副本时。即使函数不支持nullpointer参数。
反对无效论点的程度取决于,包括它取决于主观观点和直觉。
干杯!
对无效或不存在的数据执行无效操作的人,只应使其系统状态变为无效。
我认为期望输入的函数检查是否为空完全是胡说八道。或者其他的价值。函数的唯一任务是根据其输入或作用域状态执行任务,而不是其他任务。如果没有有效的输入,或者根本没有输入,那么甚至不要调用函数。此外,空检查不会检测到其他数百万个可能的无效值。你知道,正手传递的是空值,所以为什么还要传递它,在另一个传递参数的函数调用上浪费宝贵的循环,在函数中比较一些指针,然后再次检查函数输出是否成功。当然,1982年我6岁的时候就已经这样做了,但是那些日子已经过去很久了。
当然,对于公共API也有一些争论,比如一些提供了防白痴检查的DLL。你知道,这些参数:"如果用户提供了空值,你就不希望你的应用程序崩溃。"真是一个非参数。首先传递虚假数据的是用户;这是一个明确的选择,除此之外别无选择。如果有人觉得这是质量,那么…比起这些事情,我更喜欢可靠的逻辑和性能。另外,程序员应该知道他在做什么。如果他对特定范围内的无效数据进行操作,那么他就没有必要称自己为程序员。我认为没有理由降低性能,增加功耗,同时增加二进制文件的大小,这反过来影响我的产品的指令缓存和分支预测,以支持这样的用户。
如果不需要空值,则不要使参数成为指针。通过使用引用,可以保证对象不会为空。
这种方法的一个副作用是,当您的库因传递无效参数而崩溃时,您往往会受到责备。
没有比Windows操作系统更好的例子了。最初,微软的方法是消除许多虚假论点的测试。结果是一个更高效的操作系统。
然而,事实是无效的参数总是被传递。对于不符合标准的程序员,或者只是使用其他函数返回的值,不应该是空的。现在,Windows执行更多的验证,因此效率较低。
如果你想让你的程序崩溃,那么不要测试无效的参数。
在我看来,被呼叫者有责任履行合同。
如果被叫方不接受NULL,则应接受assert。
否则,当被呼叫者被交给NULL时,应该表现得很好。也就是说,它在功能上应该是一个no-op,返回一个错误代码,或者分配它自己的内存,这取决于您为它指定的契约。从呼叫者的角度来看,它应该做任何看起来最明智的事情。
作为API的用户,我希望能够在不发生程序崩溃的情况下继续使用它;我希望至少能够恢复或者在最坏的情况下优雅地关闭它。
- 但在一般情况下,被呼叫者不能执行合同。如果你得到一个坏指针,你得到一个坏指针。
- 被呼叫者绝对不能像这样执行合同:s应指向至少由T类型的n对象组成的数组。检查1%的可测试的违反合同的行为,忽略99%的不稳定的违反合同的行为是无稽之谈。我是否还应该编写代码来测试指针是否与函数的任何局部变量的地址相等?毕竟,这些指针也是可测试的无效指针。
- 而greyfade,在最后一段中,您似乎将操作环境中的异常情况(fs满、内存不足、网络中断等)导致的错误与编程错误混淆了。在前一种情况下,当然一个健壮的程序需要能够优雅地处理它们。在后者中,一个健壮的程序首先无法体验到它们。
I don't think it's the responsibility of the callee to deal with such things
如果它不承担这一责任,可能会产生不良的结果,比如取消引用NULL指针。问题是它总是含蓄地承担这个责任。这就是为什么我更喜欢优雅的操控。
是的,您应该检查空指针。你不想让一个应用程序崩溃,因为开发人员把事情搞砸了。
开发时间+运行时性能的开销与您正在设计的API的健壮性有一个权衡。
如果要发布的API必须在调用例程的进程内运行,则不应检查是否存在空参数或无效参数。在这种情况下,如果崩溃,客户机程序就会崩溃,使用API的开发人员应该改进他的方法。
但是,如果您提供的运行时/框架将在其中运行客户端程序(例如,您正在编写虚拟机或可承载代码或操作系统的中间件),则必须检查传递的参数的正确性。你不希望你的程序因插件的错误而受到指责。
在这种情况下,法律责任和道德责任是有区别的。作为一个类比,假设你看到一个视力不好的人朝悬崖边走去,却不知道它的存在。就你的法律责任而言,如果你没有警告他,而他继续走着,从悬崖上摔下来,然后死去,那么一般情况下就不可能成功地起诉你。另一方面,你有机会警告他——你可以拯救他的生命,你故意选择不这样做。一般人会轻视这种行为,认为你有道德责任去做正确的事情。
这如何适用于手头的问题?简单——被调用者对调用者的行为不负有"法律上的"责任,不管是愚蠢的还是其他的,比如传入无效的输入。另一方面,当事情发展到崩溃的时候,你的功能中的一个简单的检查可以让调用者从自己的愚蠢中解脱出来,你最终会为所发生的事情分担一些道德责任。
当然,这是一种权衡,取决于支票的实际成本。回到这个类比,假设你发现这个陌生人正缓慢地向世界另一边的一个悬崖缓缓移动,并且通过花你的毕生积蓄飞去警告他,你可以救他。在这种情况下,如果你忽视了这一点,很少有人会对你做出完全严厉的评价(假设电话并不是为了这个类比而发明的)。然而,在编码方面,如果检查和检查NULL一样简单,那么如果您不这样做,即使这种情况下的"真正的"责备是由调用者造成的,您也可以放心。
- 但空头支票真的让他们免于愚蠢吗?如果他们崩溃了,那么他们知道他们的程序中有一个bug。如果你默默无语地失败了,那么他们可能只会把坏代码留在那里。
- @比利:是的,确实如此——因为如果我检查NULL,那么我可以抛出一个异常,以防止由于调用方的输入而在我自己的代码中出现未定义的行为。调用方将能够立即检测发生了什么,可能是因为它们捕获了异常,也可能是因为程序由于未捕获的异常而崩溃。在这两种情况下,行为都是明确的。如果我只是假设他们的输入是有效的,我自己的代码可能会无声地"工作",他们也不会更聪明。
- 关键是我绝对不是在提倡默默地失败,我是在提倡检查我是否失败,如果是这样的话,我会发出一声粗俗的隆隆声。另一方面,我主张"在无效输入面前做正确的事情",这实际上是可行的。有时输出警告(告诉调用者他们需要修复问题)是可行的,但同时做一个默认的假设。
- 最后一点是,如果我总是在出错时抛出异常,而调用方知道这一点,那么他们可以安排捕获异常(如果以前没有这样做),并找出问题所在。如果我在函数内部做了一些未定义的事情而崩溃,他们唯一的选择就是找到我。不吸引人,还是谢谢你。