为什么推荐你使用 assert

关注微信公众号塔容万物

如何确保程序按照你的预期运行

C语言编程中,经常由于各种想不到的原因导致程序崩溃,并且崩溃的时候没有详细的错误信息。很难定位到错误的位置。一个经常使用的方法是将代码中可能出错的地方,全部通过print打印出来,然后再运行程序,看看哪里出错了。

void
resetToZero(int i)
{
  // 在这里修改i的值,使得i的值为0
  while (i > 0) {
    i--;
    fprintf(stderr, "[DEBUG]: i = %d\n", i);
  }
  printf("i = %d\n", i);
}

运行程序可以通过打印的结果来对程序进行debug

$ gcc -o main main.c
$ ./main
[DEBUG]: i = 9
[DEBUG]: i = 8
[DEBUG]: i = 7
[DEBUG]: i = 6
[DEBUG]: i = 5
[DEBUG]: i = 4
[DEBUG]: i = 3
[DEBUG]: i = 2
[DEBUG]: i = 1
[DEBUG]: i = 0
i = 0

当定位到问题以后,修改原来的代码,删除print语句,完成修复。但是当程序规模变大以后,这种方法需要维护的print语句太多,而且每次修改完代码以后,还需要删除print语句,非常麻烦。如果你之前看过我的这篇C语言宏的文章,你可能会想到使用宏来解决这个问题。

// 定义一个宏,如果定义了NDEBUG,就什么都不做
#ifndef NDEBUG
#define debug_print(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug_print(...)
#endif

void
resetToZero(int i)
{
  // 在这里修改i的值,使得i的值为0
  while (i > 0) {
    i--;
    debug_print("[DEBUG]: i = %d\n", i);
  }
  printf("i = %d\n", i);
}
$ gcc -o main main.c
$ ./main
[DEBUG]: i = 9
[DEBUG]: i = 8
[DEBUG]: i = 7
[DEBUG]: i = 6
[DEBUG]: i = 5
[DEBUG]: i = 4
[DEBUG]: i = 3
[DEBUG]: i = 2
[DEBUG]: i = 1
[DEBUG]: i = 0
i = 0

通过-DNDEBUG选项,可以关闭debug_print语句

$ gcc -DNDEBUG -o main main.c
$ ./main
i = 0

通过上面这种方式,通过宏控制函数,避免了手动删除printf语句。

assert函数

在标准库中,有一个assert函数,通过#include <assert.h>导入使用,在这个头文件中,assert其实是一个宏函数,他的定义与我们上面的定义类似。下面是一个简化后的assert的定义

#ifndef NDEBUG
#define assert(expr) \
  ((expr) ? (void)0 : __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))

#else
#define assert(expr) ((void)0)
#endif

assert的作用是,如果表达式expr为真,就什么都不做,如果表达式expr为假,就会打印出错误信息,并且终止程序的运行。将上面的函数修改为assert的形式,如下所示

void
resetToZero(int i)
{
  // 在这里修改i的值,使得i的值为0
  while (i > 0) {
    i--;
  }

  // 确保i的值为1
  assert(i == 1);

  // 做其他的事情,比如打印i的值
  printf("i = %d\n", i);
}

通过在关键的地方加入assert,可以当程序没有按照预期运行的时候,主动打印错误位置,然后终止程序的运行。这样就可以很方便的定位到错误的位置,而且不需要手动删除print语句。

$ gcc -o main main.c
$ ./main
main: main.c:13: resetToZero: Assertion `i == 1' failed.
[1]    82393 IOT instruction  ./main

总结

在很多时候,程序不会报错,可以正常运行,但是程序运行的结果并不是你想要的结果,原因就是可能由于代码存在的bug,是的程序阴差阳错的运行了起来,可是其中的数据全部都是错误的,通过在一些关键位置添加上assert,在程序运行过程中,当数据与预期不符合的时候,就会终止程序的运行,这样就可以很方便的定位到错误的位置,然后修复bug。