ASSERT在C语言中有什么作用?

ASSERT在C语言中有什么作用?,第1张

所有的assert都是断言,意思是程序运行到此处时,assert()内的表达式必须返回真,否则程序会抛出错误并停止运行。

  在debug编译模式时才产生代码,在release编译时,assert()会直接被忽略。

#ifdef __cplusplus

extern "C" { /如果定义了cplusplus标识符就加个括号,表示是一个外部的C函数/

#endif

#undef assert /取消定义assert标识符/

#ifdef NDEBUG /如果定义NDEBUG标识符,就启用下面的语句/

#define assert(exp) ((void) 0) /用(void) 0来替换assert函数定义,实际是取消assert函数功能,因为void 0不会生成任何语句/

#else /否则的话/

void CVIANSI _assert(char , char , int); /引入系统函数_assert的定义/

#define assert(exp) ((exp) (void) 0 : _assert(#exp, __FILE__, __LINE__))

/用 ((exp) (void) 0 : _assert(#exp, __FILE__, __LINE__))替换assert(exp)函数定义/

/替换后作用是如果exp为真,则执行(void) 0不生成任何语句,如果为假,则执行_assert语句/

/_FILE_,_LINE_是编译器常数,编译器在这一句中会将当前源程序的行数和文件名当做函数的参数放到语句中去,然后再编译这个语句/

#endif

#ifdef __cplusplus

}

#endif

#endif / _ASSERT_H_ /

if是说,这个case和else的case都有可能,而且都我能处理的

assert是说,这是个我不能处理的情况;换句话说,要想用我这个函数,必须的保证assert的东西为真,不然我不能处理

assert是包含在头文件<cassert>中的宏定义,如下:

#ifdef NDEBUG

#    define assert(condition) ((void)0)

#else

#    define assert(condition) /implementation defined/

#endif

由此可见,当NDEBUG已经定义时,assert没有任何作用。反之,condition为零时,assert会终止程序。

通常,debug(调试)模式下不会定义NDEBUG;而release(发布)模式下,会定义NDEBUG。由此可知,assert是用于调试的命令。

一般而言,在编写程序时,我们会假设输入、输出满足一定的条件。由于程序员的疏忽,我们不太可能一次就写对程序。当出现错误时,我们所做的假设可能会不成立。如果我们能将所有假设都用assert进行判断,那么程序出错时,我们能在第一时间发现问题所在。另一方面,我们希望对假设条件的判断不会影响程序的效率。因此,当我们确信程序没有错误时,我们希望assert什么都不做。请看如下示例:

#include <cassert>

template<typename T>

T multiply(T const& a, T const& b) {

    return a  b;

}

template<typename T>

T div(T const& a, T const& b) {

    return a / b;

}

int main() {

    double const a = 1, b = 2;

    assert(multiply(div(a, b), b) == a);

    return 0;

}

其中定义了multiple(乘法),div(除法) *** 作。表面上,我们并没有什么错误,不过为了保险起见,我们使用了assert进行验证。结果正如我们希望的,没有任何错误。然而,当我们将a, b的类型改为int时,程序出错了(debug模式)。因为,div的定义其实是有问题的(相信楼主能看出来吧^^)。

总之,每当我们对程序有任何假设、期望,都应该将它转化为assert语句写入程序当中。它能帮助我们定位错误的位置,缩短调试时间,百利而无一害。

PS: 需要注意的是assert应该用于处理程序中的逻辑错误,而不是输入错误。因此判断文件打开是否出错,应该使用异常等错误处理机制,而不是assert。

#include<stdioh>

int isss(int num);

int main()

{

    int num;

    while(1)

    {

        printf("输入一个数:");

        scanf("%d",&num);

        if(isss(num))

            printf("%d是素数\n",num);

        else

            printf("%d不是素数\n",num);

    }

    return 0;

}

int isss(int num)//判断是否是素数 是返回1 否返回0

{

    int i;

    if(num==0||num==1)

        return 0;

    for(i=2;i<num;i++)

    {

        if(num%i==0)

            return 0;

    }

    return 1;

}

assert():

  assert这个函数在php语言中是用来判断一个表达式是否成立。返回true or false;

例如

<php

$s =123;

assert("is_int($s)");

>

从这个例子可以看到字符串参数会被执行,这跟eval()类似。不过eval($code_str)只是执行符合php编码规范的$code_str。assert的用法却更详细一点。

assert_option()可以用来对assert()进行一些约束和控制;

如果按照默认值来,在程序的运行过程中调用assert()来进行判断表达式,遇到false时程序也是会继续执行的,这在生产环境中这样使用是不好的,而 在开发调试环境中,却是一种debug的不错的方式。特别是用上callback的方法,可以知道具体的出错信息。

所以,php的官方文档里头是建议将assert用来进行debug,我们可以发现还有一个开关ASSERT_ACTIVE可以用来控制是否开启debug。

  现在问题就产生了,如果程序员在开发的时候在代码中留下了很多assert(),然后在程序发布的时候关闭执行,设置assert_options(ASSERT_ACTIVE,0);这样做是否可行?有没有安全问题?

  我的建议是,既然assert主要作用是debug,就不要在程序发布的时候还留着它。在程序中用assert来对表达进行判断是不明智的,原因上文说了, 一个是在生产环境中assert可能被disabled,所以assert不能被完全信任;二是assert()可以被继续执行;而如果在生产环境让ASSERT_ACTIVE=1,那这个表达式字符串可以被执行本身就存在安全隐患。

eval():该函数对于在数据库文本字段中供日后计算而进行的代码存储很有用。

注意:1eval()里必须是字符串;2eval()里的引号必须是双引号,因为单引号不能解析字符串里的变量$str;

定义和用法:

(1)eval() 函数把字符串按照 PHP 代码来计算(计算=执行)。

(2)该字符串必须是合法的 PHP 代码,且必须以分号结尾。

(3)如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。

 技巧1:记住ASSERT的定义

 对许多开发人员来说,断言是一个令人困惑的话题,因为它们的许多使用方式与其设计初衷背道而驰。我见到的最清晰的断言定义是这样的:

 “断言是在程序某个特定点的一个布尔表达式,除非程序中有缺陷(Bug),否则它的值将为真。”

 想要理解上述断言定义的开发人员应该留意下面三个要点:

 ·断言会评估一个表达式是真还是假

 ·断言是在代码中的某个点对系统状态的一种假设

 ·断言会验证系统假设,如果不为真,就表明代码中有一个缺陷

 技巧2:使用ASSERT验证函数的先决条件

 断言非常适合契约式设计环境,在这种环境中,开发人员非常清晰地定义了某个函数的先决条件。断言可以用来检查该函数的输入是否满足先决条件。就拿图1所示的代码片段为例:

 图1:函数的先决条件

 函数的STate输入应该在定义的系统状态范围内。如果State不是有效的状态值,那么它就不是错误,而是缺陷!断言可以用来验证State是有效的假设,如图2所示:

  图2:对函数先决条件应用断言

 在State不小于最大值的事件中,断言表达式将被评估为假,程序于是将停止执行。停止程序执行可以让开发人员很容易马上看到哪里的代码出错,而不是过段时间以后才知道。

 技巧3:使用ASSERT验证函数的后置条件

 断言也能用来验证契约式设计环境中对某个函数输出的假设。例如,如果前面定义的System_StateSet函数返回SystemState变量,开发人员可以预计它也在期望的范围之内。断言可以用来对缺陷进行监视,如图3所示。

 图3:对函数后置条件应用断言

 开发人员在查看上述代码后可能会感到这些检查毫无意义。刚刚才设置好的SystemState怎么就会出现大于SYSTEM_STATE_MAX的值呢?答案是这确实不应该出现,然而有时候会莫名其妙地发生改变,也许是通过中断或并行线程,此时断言可以立即标志出这个缺陷。

 技巧4:不要把ASSERT用于错误处理

 在记住断言定义之后,开发人员应该切记:断言是用于检测缺陷的,不能用于错误处理。错误处理是设计用于响应错误的用户输入和意外的事件顺序的软件。错误在系统中预料是会发生的,但仅仅是因为有无效的输入而并不意味着代码中有缺陷。错误处理应该与缺陷寻找分开来。错误使用断言的一个典型例子是,在试图打开一个文件用于读取时去检查文件的指针,如图4所示。

 图4:ASSERT的不当使用

 读者可以清楚地看到,试图打开文件的结果与文件系统的状态和用户数据有关,而与代码中的缺陷一点关系也没有。开发人员应该编写错误处理程序,而不是用断言,以便在文件不存在时,错误处理程序可以用一些默认可用数据来创建它,以便后续代码继续 *** 作。

 技巧5:ASSERT仅对开发有意义,不能用于生产

 开发ASSERT宏的原始意图是在开发过程中启用它,在后面生产时要禁用。可以用NDEBUG宏激活和禁用ASSERT。正确实施的断言在被禁用后应该对嵌入式系统基本没有影响。

 问题是,如果测试是在断言启用的情况下进行的(为了捕捉任何缺陷,应该这样做),那么现在禁用断言将导致交付的产品与测试的产品处于不同的状态。断言确实会占用一些代码空间,但更重要的是,它们需要占用少量的时钟周期来评估它们的布尔表达式。禁用ASSERT可能对具有有限资源的裸机系统的执行时序产生很大影响,从而导致在生产系统中产生新的缺陷。开发团队需要判断是否值得冒关闭断言的风险。

 一种替代方案是保留断言在激活状态,而将它们的输出重定向到一个系统日志。这样可以确保任何挥之不去的缺陷很容易被识别,而且能避免中止系统的运行,而中止系统可不是明智之举。

 技巧6:不允许断言有副作用

 ASSERT的默认实现允许开发人员包含一段可执行代码作为布尔表达式的一部分。举例来说,一个状态变量可以被实现为表达式的一部分并传递给ASSERT。但如果传递给ASSERT的表达式有副作用,也就是说,它会改变嵌入式系统的状态,那么禁用断言将改变系统的行为。开发人员应该确保他们的表达式没有副作用,否则他们需要冒险在系统中增加只针对产品代码唤醒的休眠时间缺陷。

 技巧7:断言应该占代码的1%至3%

 每个开发人员对于代码库(Code Base)中应该有多少个断言都有自己的主见。大家一致同意的一个数字是,代码库中的断言占比应该大于0。断言为开发人员提供了一种在代码库中发生缺陷的时刻发现它的好方法。调试是在开发嵌入式系统中最浪费时间并令人沮丧的事情之一。不管开发人员认可的占比是1%、3%还是5%,使用断言肯定对你有利,并会使开发嵌入式软件变得多少有些趣味。

 技巧8:将断言用作可执行代码注释

 断言可以生成极好的注释!编写出色的表达式可以确切地告诉开发人员在代码的某个给定点应该预料发生什么事情。开发人员应该做好他们断言的架构,帮助人们更清楚地理解系统中发生的事情,进而帮助减少缺陷。

 小结

 断言是一种出色的工具,但有太多的嵌入式软件开发人员忽视了这一工具。本文讨论的八个技巧只是如何正确使用断言的冰山一角。接下来读者就可以在测试平台中建立和开始使用断言,并研究它们在实际的嵌入式系统中是如何工作的。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/12153106.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-21
下一篇2023-05-21

发表评论

登录后才能评论

评论列表(0条)

    保存