如何在此函数中使用assert corectly?

时间:2017-05-18 02:21:35

标签: c

我有以下功能,我在几个地方使用assert。我想知道我在哪里以错误的方式使用断言以及为什么。第一个是错误的,因为我们不能在用户输入上使用assert。 第二个:我们可以使用assert来检查malloc是否成功? 其余的我仍然无法弄清楚为什么。你能帮忙吗? 我想简要解释为什么assert在给定的地方好或坏。

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <malloc.h>

#define num 10
int hh(char *);

2 个答案:

答案 0 :(得分:3)

你定义了什么是对错,所以这取决于你,但作为一般规则你不希望你的程序逻辑在断言中(因此检查输入字符串的长度是一个很差的用法)。

请记住:断言仅在调试模式下处于活动状态,因此如果您依赖它进行错误检查(就像几乎所有示例一样),您将在发布模式中发生奇怪的事情。

即。 assert通常是一个定义,如:

/* assert() only does something if NDEBUG is NOT defined */
#ifdef NDEBUG
#else
#define assert(x) _assert(x)
#endif

有关NDEBUG

的更多信息,请参阅Where does the -DNDEBUG normally come from?

你绝对不想使用断言改变流程,例如。

assert(some_function()); /* some_function only called if NDEBUG is not defined */

使用assert检查malloc的返回:如果未定义NDEBUG,则检查未完成,因此理论上您可以关闭并访问NULL指针。我说它不安全。请参阅Handling error checking with assert更多讨论。

让我们通过简单的“仅在调试中”过滤器逐一查看您的断言:

assert(argc==2);
/*
 * If NDEBUG is defined and argc == 1 you get through,
 * so anything that uses argc[1] will give undefined behavior.
 * = BAD USE
 */
 char *s = malloc(N);
 /* 2 */
 assert(s!=NULL);
 scanf("%s", s
 /*
  * If NDEBUG is defined and malloc fails you keep going and read into 
  * NULL = undefined behaviour = BAD
  */

 assert(strlen(s)<N);
 /*
  * If NDEBUG is defined keeps going even in strlen >= N.
  * How bad that is depends on the code - if you are checking the size
  * before putting it in a buffer, then it's bad.
  * To me it is logic in the assert and so I say BAD
  */
 /* 4 */
 assert(!*(s+strlen(s)));
 /*
  * First of that's just silly code.
  * If that comes to my code review you'll be removing or rewriting
  * it - strlen relies on the NUL, so how can it fail?
  * It is logic in the assert - If NDEBUG is set you'll keep going
  * even if the condition is not met. How bad that is?
  */
 /* 5 */
 assert(atol(s));
 /*
  * Whoa. If NDEBUG is set and s = "Hello" we still keep going...
  * What is atol("Hello") ?
  */
 printf("%ld\n", 100000000/atol(s));
 free(s);
 return 0;
}
int f(char *s)
{
 /* 6 */
 assert(s!=NULL);
 /*
  * Again if NDEBUG is defined the check disappears and so if s = NULL 
  * then you dereference a NULL pointer which is undefined behavior
  */
 return !*s;

答案 1 :(得分:2)

我希望这个答案通过努力查看断言示例的有意义使用来提供关于断言的替代视图。我否认同意谴责大部分答案的答案。

断言用于验证假设,即断言它们是真的 将断言想象为

  • 一种自我记录的方法,说明了 &#34;在下面的代码中,我盲目地假设以下表达式始终为真。如果不是这种情况,我控制之外的东西是非常错误的,而且这段代码无法完成它应该做的事情。&#34;
    这种自我文档总是有效,无论断言是否有效。
  • 技术实现,如果处于活动状态,将在运行时验证表达式是否为真,或者在发生故障时将响铃警报;
    即它将使用一些适当的方法来引起某人的注意 (这种获得关注的方法可能差异很大;在深度嵌入式平台上,该方法可以变得非常有趣......)
    请注意,断言通常以可以取消激活的方式实现。这样做的目的是消除他们的时间消耗,代码大小的消耗以及可能产生令人困惑的输出。因此,有时在交付软件之前,在开发/测试结束时停用它们。获得注意力的方法可能不仅令人不愉快,实际上也可能是危险的。想想飞机上的软件,在空中击中一个失败的假设......在某个地方的软件中可怕而且绝对错误。但是,在空中,您不希望在某处巧妙地显示有用的调试信息。你只是希望飞机能够吸收它并以某种方式尽快恢复正常运行 因此,技术实现部分仅对系统发生的问题有用,即可以假设在正常使用中可能发生的事情也将在测试/开发情况中发生。对于大多数违反规范的情况,就是这种情况。对于短暂或罕见的问题,即仅在不可预测的情况下出现的问题,情况并非如此。

单独查看断言示例:

  1. 盲目地假设输入参数的数量是2似乎有些大胆 但是,如果程序的环境定义非常严格,那么这是可以想象的。您的程序可能是管道设置的一部分,或者只能从makefile中调用。对输入参数数量的断言将反映出更大的体系结构范围,并支持由众多开发人员维护的系统中的错误检测。
    但是,通过检查程序代码并在终止失败之前提供有用的错误消息,可以更好地实现这一目的。
  2. 盲目地假设malloc没有返回零是一个广泛传播的错误。使用断言进行明确说明,这至少会检测并发出问题的信号,实际上是对使用malloc而不进行检查的改进。由于系统内存在异常情况下耗尽而引起的malloc故障是一个问题的例子,这个问题无法通过断言有效地检测到,即不能系统地&#34;而不是&#34;滥用规范& #34 ;.
    同样,检查程序代码,使用合适的错误消息和不成功的终止,会更好。
  3. 盲目地假设最大输入长度是大胆的。什么应该阻止人们听从这个规则?特别是如果没有输出禁止更长的输入 像1一样,这在一个严格定义的大型架构中是可以想象的,人类不会妨碍这种架构 同样,即使对于作为用户的make工具,使用错误消息检入代码也会更好。
  4. 我同意John的看法,我根本看不出这种断言是如何失败的 断言应该描述C语法和语义实际上可能失败的假设。相比之下,我看到assert(false)的次数比我想象的要多。这至少使我感觉到了#Iv;我盲目地认为运行时执行永远不会在这里结束。&#34;但其他方面很奇怪,需要广泛(通常缺失)的评论解释。
  5. 另一个&#34;人类待在外面&#34;假设,这可能在make的背景下有意义 在运行时更好地检查和报告错误。
  6. 这是函数开头的典型断言,它使用指针并依赖于&#34; promise&#34;它只会被非NULL指针调用。所以在规范的某个地方,应该有一些&#34;这个函数只能用非NULL指针调用。&#34;。
    在运行时使用错误消息和不成功终止进行检查仍然是明智的,以便在断言断言时对NULL指针也是健壮的。然而,这可能被视为浪费时间和代码大小;与对代码质量,规范表达和测试深度的信心相比,两者都更加昂贵。