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

Bil*_*eal 37 c c++ null

这个问题的灵感来自于这个答案.

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

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

如果一个函数没有在它的文档中说它有效通过nullptr,那么你最好不要传递nullptr给那个函数.我不认为被叫方有责任处理这类事情.

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

R..*_*R.. 43

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

如果您将错误代码返回给已经证明自己错误的调用者,最可能的结果是调用者将忽略该错误,并且当错误的原始原因变得困难或者错误的原始原因发生时,错误的事情将在稍后发生.无法追查.为什么假设调用者会忽略您返回的错误是合理的?因为调用者已经忽略了错误返回malloc或者fopen其他一些特定于库的分配函数,这些函数返回NULL表示错误!

  • 拥有NULL指针不是一个错误.将它传递给记录为需要指向数据的指针的函数是一个错误.我已经解释了为什么返回错误可能会被忽略. (25认同)
  • 在一种非常不正常的"工作"意义上.这大致相当于为`SIGSEGV`和`longjmp`'设置信号处理程序......调用者已经证明它有重大错误,并且不能保证它的数据还没有下地狱. (10认同)
  • 放置`assert()`但没有条件`return`的缺陷是:如果你没有测试所有可能的执行代码的方法(在指针可能是危险的nullptr的所有精确位置),那么你可能有应用程序崩溃.具有条件返回允许应用程序继续工作而不会发生崩溃. (3认同)
  • @JamieBullock:这不是个好主意.滥用`assert`会在调试时隐藏问题(`assert`无法打印实际的断言,`input!= NULL`,它会立即显示错误是什么)和`return;`隐藏bug如果它设法使其成为没有启用断言的生产构建.你想在这里崩溃**,通过`assert`或空指针取消引用. (3认同)

Kar*_*tel 28

在C++中,如果你希望接受NULL指针,然后不抓住这个机会:接受一个引用,而不是.

  • 我尽可能这样做.但偶尔我需要公开一个C API,这让我处理指针:) (5认同)
  • @R ..,"现实是函数中的机器代码将看到一个NULL指针并将取消引用它." - 不 取消引用在调用函数之前发生,并且是未定义的行为.事实上,指出未定义行为的典型例子是未定义的行为与"引用无用的正式规范"相差甚远,因为我可以想象它可能得到.至于"隐藏被调用者可能修改数据的事实",我已经在互联网上其他地方的许多页面讨论了这个论点,并且仍然发现这个位置充其量是可疑的. (5认同)
  • @Karl Knechtel:问题在于你真的*允许你的图书馆作者定义"未定义的行为",因为否则你可以传递一个已经释放的函数指针,然后声称任何后续崩溃是被调用函数的错误. (5认同)
  • @R ..,这根本不是真的; 在许多情况下,编译器可以完全优化引用,但不能使用指向间接的优化来"优化"等效代码.至于崩溃发生的地方,这与**编写破碎代码**的问题无关.你无法合理地防止未定义的行为"但是在其他人的代码运行之前不会发生崩溃",就像你无法合理地防止语法错误一样"但是在编译器尝试之前错误不明显编译它". (4认同)
  • 同意.C++中指针的语义是它可以是NULL.如果它不能为NULL,则使用引用.你没有,因此允许NULL. (2认同)
  • 我不同意.在C++中传递引用隐藏了被调用者可能修改数据的事实,并且当它们用于写入时应该被认为是有害的.传递指针绝不意味着NULL有效.而且,引用**实际上可以为null,例如`function_that_takes_reference(*ptr);`其中`ptr`是'NULL`.(不要引用无用的正式规范;实际​​情况是函数**中的机器代码将看到NULL指针**而**将取消引用它**.) (2认同)
  • 无论哪种方式,从调试的角度来看,崩溃都将在库函数内部而不是在调用程序中发生。我不在乎C ++规范试图假装引用不仅仅是指针的语法糖。实际上,它们是同一回事。 (2认同)

Nem*_*vic 14

虽然一般情况下我没有看到检测NULL(为什么是NULL而不是其他一些无效地址?)的公共API的价值我可能仍然只是因为许多C和C++程序员期望这样的行为.


Ste*_*end 8

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

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

  • @Steve:[IsBadXxxPtr应该真的叫做CrashProgramRandomly](http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx).这实际上是我问这个问题的另一个原因:) (8认同)
  • 您通常无法检查无效参数.检查NULL只能防止极小的错误子集.我的断言是因为库一般不能防止滥用该函数,并且该函数无论如何都会因此类错误而崩溃,最好导致错误发生的崩溃,而不是将其变成错误代码可以忽略.但是,断言建议为+1. (4认同)

And*_*Eve 6

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

除非我对调用者有完全的控制权并且我必须优化即使是微小的速度改进,我总是检查NULL.

  • 如果最小化核心转储的可能性意味着增加用腐败废话覆盖磁盘上文件的可能性怎么办? (4认同)
  • 请定义"健壮性",请:) (3认同)