7

断言用于检查是否满足条件(前置条件、后置条件、不变量)并帮助程序员在调试阶段发现漏洞。

例如,

void f(int *p)
{
  assert(p);
  p->do();
}

我的问题是我们是否需要假设在发布模式下无法满足条件并相应地处理案例?

void f(int *p)
{
  assert(p);

  if (p)
  {
    p->do();
  }
}

毕竟,断言意味着它测试的条件永远不应该是错误的。但是,如果我们不检查它并且它失败了,程序就会崩溃。听起来是个两难的选择。你们是怎么处理的?

4

8 回答 8

24

如果断言失败,程序应该会崩溃

断言失败意味着程序员在理解程序流如何继续进行时犯了一个根本性错误。这是一种开发援助,而不是生产援助。在生产中,人们可能会处理异常,因为它们“可能”会发生,而断言应该“永远不会”失败。

如果您在阵营中说:“哦,但是如果断言在生产中失败怎么办?我需要抓住它们!” 那么你就错过了重点。问问自己,在这种情况下,为什么不只是抛出异常(或以其他方式处理错误)?

一般来说,assert不仅仅是“如果条件不满足,则抛出异常”的简写(嗯,有时这是操作语义,但不是指称语义)。相反,断言失败意味着应用程序处于开发人员认为甚至不可能的状态。您真的希望代码在这种情况下继续执行吗?显然(我会说),

于 2010-10-29T05:59:00.557 回答
2

防御性编程总是最好的。您应该始终假设,尽管您进行了所有测试,但您的应用程序仍会出现错误。因此,在您可以避免 NULL 指针引用并继续前进的情况下,添加 NULL 检查符合您的最佳利益。

但是,在某些情况下根本没有简单的方法可以避免崩溃,在这些情况下,断言是您在开发周期中检测问题的唯一方法。

不过,重要的一点 - 断言也经常用于检测数据完整性的主要问题。如果您继续通过这些断言,您可能会冒损坏数据的风险。在这些情况下,最好是崩溃而不是破坏您的数据。(显然,任何类型的崩溃处理程序至少会提供一个带有错误描述的合理 UI 将是更可取的)。

于 2010-10-29T05:59:58.663 回答
1

严格来说,第二个代码是有冗余的。

void f(int *p)
{
  assert(p);
  if (p)    // Beats the purpose of assertion
  {
    p->do();
  }
}

断言意味着发生了错误。出乎意料/未处理的事情。在上面的代码中,要么

1)您正在正确处理 p 为空的情况。(通过不调用 p->do())- 这应该是正确/预期的事情。然而,这样的断言是虚惊一场

2) 另一方面,如果不调用 p->do(),会出现问题(可能在代码或输出中更进一步),那么断言是正确的,但无论如何继续都应该没有意义。

在上面的代码中,程序员正在加倍努力地处理无论如何都是错误的情况。

也就是说,有些人喜欢将断言视为出现问题,但让我们看看我们是否仍然得到正确的输出。IMO,这是一个糟糕的策略,并在错误修复期间造成混乱。

于 2010-10-29T07:47:54.180 回答
1

断言是调试代码,而不是操作代码。不要使用它们来捕获输入错误。

于 2010-10-29T05:59:04.807 回答
0

由于很多人都在评论将断言置于发布模式:

在我的工作中,效率非常重要(有时,大型数据集的执行需要几十个小时到几天才能完成)。因此,我们有只在调试代码中断言的特殊宏(在 QA 等期间运行)。例如,for 循环中的断言绝对是一种开销,您可能希望在发布代码中避免它。毕竟,如果一切顺利,断言不应该失败。

发布代码断言很好的一个例子是——如果逻辑根本不应该命中特定的代码分支。在这种情况下,assert(0) 很好[因此任何类型的 assert(0) 都可以始终留在发布代码中]。

于 2010-10-29T07:53:56.453 回答
0

断言用于捕获测试中的错误。理论是你已经测试得足够好,知道一旦你发布它就会工作。

如果在实际操作中可能出现这种情况,请不要依赖断言 - 使用异常或其他一些错误机制。

于 2010-10-29T05:59:34.427 回答
0

正如您提到的,断言对于调试很有用。他们永远不应该把它变成生产代码(编译后,当然可以将它们包装在#ifdefs中)

如果您遇到无法纠正的问题并且您需要检查生产代码,我会执行以下操作:

void f(int *p)
{

  if (!p)
  {
    do_error("FATAL, P is null.");
  }

  p->do();
}

其中 do_error 是一个记录错误并干净退出的函数。

于 2010-10-29T05:59:46.270 回答
0

我说将它们留在 à release build 中。任何构建中都必然存在错误。在您的产品中包含断言意味着当您收到来自 à uwer 的问题报告时,您可以更轻松地查明问题。

不要花太多精力来处理异常。只需确保您可以获得完整的异常,包括堆栈跟踪。这尤其适用于 à release build。

于 2010-10-29T06:01:50.450 回答