首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C中使用assert()的位置。C++?

在C中使用assert()的位置。C++?
EN

Stack Overflow用户
提问于 2012-02-02 14:19:25
回答 4查看 7.3K关注 0票数 22

我们应该在哪些地方特别使用assert()函数?如果是像判断整数值是否大于零或者指针是否为空这样的情况,我们可以简单地使用一个私有函数来检查。在这种情况下,我们应该在哪里使用assert()而不是自定义的书面检查?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-02-02 16:40:44

背景:我写服务器软件是为了谋生,这种软件在下一个版本加载之前会持续几个星期。所以我的回答可能偏向于高度防御性的代码。

:The principle。

在我们深入研究在哪里使用assert的细节之前,理解它背后的原则是很重要的。

assert是防御性编程中必不可少的工具。它有助于验证假设(实际断言它们),从而捕获编程错误(与用户错误区分)。assert的目标是检测错误情况,这些情况通常不可能立即恢复。

示例:

代码语言:javascript
复制
char const* strstr(char const* haystack, char const* needle) {
  assert(haystack); assert(needle);

  // ...
}

替代方案。

用C语言?几乎没有其他选择。除非您的函数被设计为能够传递错误代码或返回标记值,而且这一点有适当的文档记录。

在C++中,异常是一种完全可接受的替代方案。然而,assert可以帮助产生内存转储,这样您就可以确切地看到程序在检测到错误情况时处于什么状态(这有助于调试),而异常将展开堆栈,从而丢失上下文(oups...)。

此外,异常可能(不幸地)被高级处理程序捕获(或者来自开发人员同事的令人不快的捕获(当然,您不会这样做)),在这种情况下,您可能会完全遗漏错误,直到为时已晚。

在哪里不使用它。

首先,应该理解assert只在调试代码中有用。在Release中,定义了NDEBUG,不会生成任何代码。作为推论,Release assert 中的与评论具有相同的价值。

  • 从不将其用于软件良好行为所必需的检查。应检查并处理错误条件。一直都是。

其次,应该理解错误的输入是你生活的一部分。你想让你的编译器在你每次出错时都显示一条assert消息吗?嗯!因此:

  • 从不将其用于输入数据验证。应验证输入数据,并将错误适当地报告给用户。一直都是。

第三,应该理解的是,崩溃是不受欢迎的。希望您的程序能够顺利运行。因此,人们不应该试图在发布模式中保留断言:发布代码最终落入最终用户手中,永远不会崩溃。在最坏的情况下,它应该在显示错误消息时关闭。预计在此过程中不会丢失用户数据,如果在重新启动时将用户带回她所在的位置,效果会更好:例如,现代浏览器就是这样做的。

  • 从不在Release中保留断言。

注意:对于服务器代码,在大多数情况下,当“命中”断言时,我们设法回到处理下一个查询的位置。

在哪里使用它。

在调试模式下,assert处于打开状态,因此应使用它进行调试。无论何时测试新代码,无论何时测试套件运行,无论何时软件在您(或您的队友)手中,无论软件在您的QA部门手中。断言允许您发现错误,并为您提供错误的完整上下文,以便您可以进行修复。

  • 在开发和测试周期中使用它。

甚至更好。由于您知道代码将不会在版本中执行,因此您可以执行昂贵的检查。

注意:如果只是为了检查性能,您还应该测试发布的二进制文件。

和发行版中的?

好吧,在我工作的代码库中,我们用特定的异常替换廉价的断言(其他的被忽略),这些异常只被一个高级处理程序捕获,该处理程序将记录问题(使用回溯),返回预编码的错误响应并恢复服务。开发团队会自动收到通知。

在部署的软件中,我看到的最佳实践意味着创建一个内存转储,并将其传输回开发人员进行分析,同时尝试不丢失任何用户数据,并尽可能礼貌地对待不幸的用户。当我考虑到这项任务的难度时,我真的很幸运能在服务器端工作;)

票数 36
EN

Stack Overflow用户

发布于 2012-02-02 14:52:20

我要抛弃我对assert()的看法。我可以在其他地方找到assert()的用途,但stackoverflow提供了一个很好的论坛,可以提供关于如何以及何时使用它的建议。

assertstatic_assert都有类似的功能。假设你有一些foo函数。例如,假设您有一个函数foo(void*),该函数假定其参数不为空:

代码语言:javascript
复制
void foo(void* p) {
  assert(p);
  ...
}

你的函数有几个人关心它。

首先,调用您的函数的开发人员。他可能只是看了你的文档,也许他会错过不允许空指针作为参数的部分。他可能永远不会读取函数的代码,但是当他在调试模式下运行时,assert可能会捕捉到他对您的函数的不当使用(特别是如果他的测试用例是好的)。

第二个(也是更重要的)是阅读你的代码的开发人员。对他来说,您的断言说此行之后的,p不是空。这一点有时会被忽视,但我相信这是assert宏最有用的特性。它记录并强制实施条件。

只要实际可行,就应该使用asserts对此信息进行编码。我喜欢把它看作是在说“在代码的这一点上,这是真的”(并且它以一种比强得多的方式表达了这一点)。当然,如果这样的语句实际上没有传达太多/任何信息,那么就不需要它。

票数 8
EN

Stack Overflow用户

发布于 2012-02-02 20:11:03

我认为这里有一个简单而有力的观点:

assert ()用于检查内部一致性。

使用它检查前置条件、后置条件和不变量。

当由于外部因素,代码无法在本地控制的情况下可能存在不一致时,则抛出异常。前置条件不能满足后置条件时,会出现异常。很好的例子:

  • new int可以满足它的前提条件,所以如果内存不可用,抛出是唯一的合理响应。( malloc的后置条件是“有效指针或NULL”)
  • 构造函数的后置条件是其不变量已建立的对象的存在。如果它不能构造一个有效的状态,则抛出是唯一的合理响应。

以上操作不应使用assert。相比之下,

代码语言:javascript
复制
void sort (int * begin, int * end) {
    // assert (begin <= end); // OPTIONAL precondition, possibly want to throw
    for (int * i = begin, i < end; ++i) {
        assert (is_sorted (begin, i)); // invariant
        // insert *i into sorted position ...
    }
}

检查is_sorted就是检查算法在给定前提条件的情况下是否正确运行。异常不是合理的响应。

长话短说:如果程序是本地正确的,assert是用于永远不会发生的事情,异常是对于可能出错的事情,即使代码是正确的。

无效输入是否会触发异常是一个风格问题。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9107969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档