大多数人使用像这样的指针......
if ( p != NULL ) {
DoWhateverWithP();
}
Run Code Online (Sandbox Code Playgroud)
但是,如果指针因任何原因为空,则不会调用该函数.
我的问题是,如果不检查NULL可能更有益吗?显然,在安全关键系统上,这不是一个选项,但是如果程序在没有它的情况下仍可以运行,那么你的程序崩溃的荣耀比没有被调用的函数更明显.
关于第一个问题,在使用指针之前是否总是检查NULL?
其次,考虑你有一个将指针作为参数的函数,并且在整个程序的多个指针上多次使用此函数.你是否发现在函数中测试NULL更有利(好处是你不必在整个地方测试NULL),或者在调用函数之前在指针上测试(这样做的好处就是调用函数没有任何开销) )?
Pie*_*aud 15
你是正确的认为NULL指针经常导致立即崩溃,但不要忘记,如果你通过NULL指针索引到一个大型数组,如果你的索引足够高,你可能确实得到一个有效的内存地址.然后,你会得到内存损坏或不正确的内存读取,这将更难找到.
每当我可以假设调用一个带NULL的函数是一个错误,这在生产代码中永远不会发生,我更喜欢在函数中使用ASSERT保护,它只在调试版本中编译成实际代码,否则不检查NULL.
从我的角度来看,通常,函数应检查其参数,而不是调用者.您应该始终假设您的呼叫者可能对检查有点草率,或者他们可能包含错误...
道德性:通过抛出的一些if()语句或使用一些ASSERT结构(可能带有明确的消息说明为什么会发生这种情况)检查被调用函数中的NULL.还要在调用者中检查NULL,但前提是调用者知道在正常的程序执行中可能会发生这种情况,并采取相应的行动.
mrk*_*rkj 10
当一个NULL
指针出现时程序只能崩溃是可以接受的,我偏爱:
assert(p);
DoWhateverWithP();
Run Code Online (Sandbox Code Playgroud)
这只会检查调试版本中的指针,因为NDEBUG
通常在预处理器级别定义#undef
s assert()
.它记录了您的假设并协助调试,但对已发布的二进制文件没有性能影响(但是,公平地说,NULL
在绝大多数情况下检查指针应该对性能实际上没有影响).
作为附带好处,这对于C和C++都是合法的,在后一种情况下,不需要在编译器/运行时中启用异常.
关于你的第二个问题,我更喜欢把断言放在子程序的开头.再一次,美丽assert()
的事实是,没有任何"开销"可言.因此,没有什么可以衡量在子程序定义中只需要一个断言的好处.
当然,需要注意的是,你永远不想断言带有副作用的表达式:
assert(p = malloc(1)); // NEVER DO THIS!
DoSomethingWithP(); // If NDEBUG was defined, malloc() was never called!
Run Code Online (Sandbox Code Playgroud)
我更喜欢这种风格:
if (p == NULL) {
// throw some exception here
}
DoWhateverWithP();
Run Code Online (Sandbox Code Playgroud)
这意味着该代码所处的任何功能都会在发生这种p
情况时迅速失败NULL
.你是正确的,如果p
是NULL
有没有办法,DoWhateverWithP
可以执行,但使用空指针或根本不执行的功能都无法接受的方式来处理fack的的p
是NULL
.
要记住的重要一点是提前退出并快速失败 - 这种方法产生的代码更容易调试.
不要只检查空值,如果找到则不做任何操作.
如果允许指针为null,则必须考虑代码在实际为空的情况下执行的操作.通常,无所事事就是错误的答案.小心翼翼地定义那样工作的API是可能的,但这需要的不仅仅是散布一些关于这个地方的NULL检查.
因此,如果允许指针为null,则必须检查null,并且必须执行任何适当的操作.
如果不允许指针为null,那么编写调用未定义行为的代码(如果它为null)是完全合理的.它与编写字符串处理例程没有什么不同,如果输入不是NUL终止的,则调用未定义的行为,或者如果调用者传入错误的值,则编写缓冲区使用的例程调用未定义的行为,或编写一个函数一个file*
参数,如果用户将文件描述符reinterpret_cast传入其中,则调用未定义的行为file*
.在C和C++中,您只需依靠调用者告诉您的内容即可.垃圾进垃圾出.
但是,您可能希望编写代码,以便在传递最可能的垃圾类型时帮助您的调用者(毕竟可能是您).断言和异常对此有好处.
从Franci对这个问题的评论中得出类比:大多数人在穿过人行道或坐在沙发上时都不会寻找汽车.他们仍然可能被汽车击中.它发生了.但是在这种情况下花费任何力气检查汽车通常会被认为是偏执狂,或者对于一罐汤的说明"首先,检查你厨房里的汽车.然后,加热汤".
您的代码也是如此.将无效值传递给函数比将汽车意外驾驶进入某人的厨房要容易得多.但是如果他们这样做并打击某人,那仍然是司机的过错,而不是厨师未能做到应有的谨慎.您不一定希望厨师(或被调用者)使用应该多余的检查来混淆他们的食谱(代码).
还有其他方法可以找到问题,例如单元测试和调试器.无论如何,除了必要的(道路)之外,创造一个无车环境要比在整个地方无所不能地驾驶汽车更安全,并希望每个人都可以随时应对它们.因此,如果你在不允许的情况下检查null,你不应该让它让人们认为它毕竟是允许的.
[编辑 - 我真的只是一个错误的例子,检查null不会找到无效的指针.我将使用地图来保存一些物体.我将使用指向这些对象的指针(表示图形),这很好,因为map永远不会重定位其内容.但我还没有定义对象的排序(这样做会有点棘手).因此,为了让事情发生变化并证明其他代码有效,我使用了向量和线性搜索而不是地图.没错,我不是指矢量,我的意思是deque.因此,在第一次向量调整大小之后,我没有将空指针传递给函数,但是我将指针传递给已释放的内存.
我做了一些愚蠢的错误,它传递无效的垃圾大致和我做出无效错误传递空指针的哑错误一样.因此,无论我是否添加了对null的检查,我仍然需要能够诊断程序崩溃的问题,因为我无法检查.由于这也将诊断空指针访问,我通常不打扰检查null,除非我正在编写代码以通常检查进入函数的前提条件.在这种情况下,如果可能的话,应该做的不仅仅是检查null.]