每次写摘要我都觉得是一件很头疼的事儿,因为我知道摘要真的很重要,它简直直接就决议了读者的数量。或许花了九六二虎之力写出来的东西,因为摘要的失利而前功尽弃,因为绝大多数的读者看文章之前都会阅读下摘要,假如他们发现摘要“不对口”,没有什么特征和吸引人的当地,那么轻则选用目下十行的办法看完全文,重则对文章判“死刑”,一篇文章的好坏尽管不能用摘要来衡量,可是它却常常被读者用来衡量一篇文章的好坏,然后成为了文章读者数量多少的一个关键因素。下面言归正传来说说断语,假如出于一般性的学习C言语,敷衍考试的话,我想很少有人会在代码中运用断语,或许有的人在此之前从来没有运用过断语。那么断语的运用究竟能给咱们的代码带来什么呢?我尽或许的把我所了解的断语的运用解说清楚,期望我在此所讲的断语能够对你有所协助,让你今后能够在代码中灵敏运用断语。
在解说之前,咱们先来对断语做一个根本的介绍,让咱们对断语有一个大致的了解。在运用C言语编写工程代码时,咱们总会对某种假定条件进行查看,断语便是用于在代码中捕捉这些假定,能够将断语看作是反常处理的一种高档办法。断语表明为一些布尔表达式,程序员信任在程序中的某个特定点该表达式值为真。能够在任何时分启用和禁用断语验证,因而能够在测验时启用断语,而在布置时禁用断语。相同,程序投入运转后,终究用户在遇到问题时能够从头重用断语。它能够快速发现并定位软件问题,一起对体系过错进行主动报警。断语能够对在体系中躲藏很深,用其它手法极难发现的问题能够用断语来进行定位,然后缩短软件问题定位时刻,进步体系的可测性。实践应用时,可依据具体情况灵敏地规划断语。
经过上面的解说咱们关于断语算是有了一个大约的了解,那么接下来咱们就来看看C言语中assert宏在代码中的运用。
原型界说:
void assert( int expression );
assert宏的原型界说在中,其效果是先核算表达式 expression ,假如expression的值为假(即为0),那么它先向stderr打印一条犯错信息,然后经过调用abort 来停止程序运转。
下面来看看一段代码:
#include
#include
int main( void )
{
int i;
i=1;
assert(i++);
printf("%d\n",i);
return 0;
}
运转成果为:
看看运转成果,因为咱们给定的i初始值为1,所以运用assert(i++);句子的时分不会呈现过错,从而履行了i++,所以这今后的打印句子输出值为2。假如咱们把i的初始值改为0,那么就回呈现如下过错。
Assertion failed: i++, file E:\fdsa\assert2.cpp, line 8
Press any key to continue
是不是发现依据提示很快就能定位犯错点呢?!已然assert这么便于定位犯错点,看来确实咱们有必要娴熟的在代码中运用它,可是什么东西的运用都是有规矩的,assert的运用也不破例。
断句子子不是永久会履行,能够屏蔽也能够启用,这就要求assert不管是在屏蔽仍是启用的情况下都不能对咱们自身代码的功用有所影响,这样的话方才咱们在代码中运用了一句assert(i++);是不当的,因为咱们一旦禁用了assert,i++的句子就得不到履行,关于接下来i值的运用就会呈现问题了,所以关于这样的句子咱们应该是要分开来完成,写出如下两句来替代, assert(i); i++;,所以这就关于断语的运用有了相应的要求,那么咱们一般在什么情况下运用断语呢?首要表现在一下几个方面:
1.能够在估计正常情况下程序不会抵达的当地放置断语。(如assert (0);)
2.运用断语测验办法履行的前置条件和后置条件 。
3.运用断语查看类的不变状况,保证任何情况下,某个变量的状况有必要满意。(如某个变量的改动规模)
关于上面的前置条件和后置条件或许有的读者还不是很了解,那么看看下面的解说你就理解了。
前置条件断语:代码履行之前有必要具有的特性
后置条件断语:代码履行之后有必要具有的特性
前后不变断语:代码履行前后不能改动的特性
当然在运用的断语的过程中会有一些咱们应该留意的事项和养成一些杰出的习气,如:
1.每个assert只查验一个条件,因为一起查验多个条件时,假如断语失利,咱们就无法直观的判别是哪个条件失利
2.不能运用改动环境的句子,就像咱们上面的代码改动了i变量,在实践编写代码的过程中是不能这样做的
3.assert和后边的句子应空一行,以构成逻辑和视觉上的一起感,也算是一种杰出的编程习气吧,让编写的代码有一种视觉上的美感
4.有的当地,assert不能替代条件过滤
5.放在函数参数的入口处查看传入参数的合法性
6.断句子子不能够有任何鸿沟效应
上面那么多的文字,好像很单调,可是没办法,咱们不能急于求成,仍是要先坚持看完文字描绘部分,这样在下面咱们剖析代码的过程中就能很快知道为什么会呈现那样的问题了,也能在自己编写代码的时分娴熟的运用assert,给自己的代码调试带来极大的便当,尤其是你在用C言语做工程项目的时分,假如你能够在你的代码中合理的运用assert,能使你创立更安稳、质量更好且不易于犯错的代码。当需要在一个值为FALSE时中止当时操作的话,能够运用断语。单元测验有必要运用断语,除了类型查看和单元测验外,断语还供给了一种确认各种特性是否在程序中得到保护的极好的办法。凡是优异的程序员都能够在自己代码中很好的运用assert,编写出高质量的代码来。
说了assert这么多的有点,当然也要说说它的缺陷了。
运用assert的缺陷是,频频的调用会极大的影响程序的功能,添加额定的开支。所以在调试完毕后,能够经过在包括#include 的句子之前刺进 #define NDEBUG 来禁用assert调用。
接下面剖析一下下面的一段代码:
#include
//#define NDEBUG
#include
int copy_string(char from[],char to[])
{
int i=0;
while(to[i++]=from[i]);
printf("%s\n",to);
return 1;
}
int main()
{
char str[]="this is a string!";
char dec_str[206];
printf("%s\n",str);
assert(copy_string(str,dec_str));
printf("%s\n",dec_str);
return 0;
}
运转成果为:
在以上代码的最初部分咱们把#define NDEBUG给注释掉了,所以咱们启用了assert,main函数中运用了assert(copy_string(str,dec_str));来完成copy_string函数的调用,在copy_string函数中咱们运用了一句return 1,所以终究的函数调用成果就等价所以assert(1),所以接下来持续履行assert下面的打印句子,终究成功的打印了三条输出句子,假如咱们把最初的注释部分翻开,成果就只能成功的输出开始部分一条打印句子。
以上咱们都是在围绕着assert宏在解说,仅仅是教会咱们怎么来运用assert宏,那么接下来看看咱们怎么来完成自己的断语呢?
接下来咱们看看别的一段代码:
#include
//#undef _EXAM_ASSERT_TEST_ //禁用
#define _EXAM_ASSERT_TEST_ //启用
#ifdef _EXAM_ASSERT_TEST_ //启用断语测验
void assert_report( const char * file_name, const char * function_name, unsigned int line_no )
{
printf( "\n[EXAM]Error Report file_name: %s, function_name: %s, line %u\n",
file_name, function_name, line_no );
}
#define ASSERT_REPORT( condition ) \
do{ \
if ( condition ) \
NULL; \
else \
assert_report( __FILE__, __func__, __LINE__ ); \
}while(0)
#else // 禁用断语测验
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
int main( void )
{
int i;
i=0;
// assert(i++);
ASSERT_REPORT(i);
printf("%d\n",i);
return 0;
}
运转成果如下:
[EXAM]Error Report file_name: assert3.c, function_name: main, line 29
0
仔细的读者会发现咱们并没有运用断语来完毕当时程序的履行,所以在断语下面的printf成功的打印出了i的当时值,当然咱们也能够做恰当的修正,在断语出发现过错,那么就调用 abort();来使当时正在履行的程序反常停止,修正如下:
#include
#include
//#undef _EXAM_ASSERT_TEST_ //禁用
#define _EXAM_ASSERT_TEST_ //启用
#ifdef _EXAM_ASSERT_TEST_ //启用断语测验
void assert_report( const char * file_name, const char * function_name, unsigned int line_no )
{
printf( "\n[EXAM]Error Report file_name: %s, function_name: %s, line %u\n",
file_name, function_name, line_no );
abort();
}
#define ASSERT_REPORT( condition ) \
do{ \
if ( condition ) \
NULL; \
else \
assert_report( __FILE__, __func__, __LINE__ ); \
}while(0)
#else // 禁用断语测验
#define ASSERT_REPORT( condition ) NULL
#endif /* end of ASSERT */
int main( void )
{
int i;
i=0;
// assert(i++);
ASSERT_REPORT(i);
printf("%d\n",i);
return 0;
}
运转成果如下:
[EXAM]Error Report file_name: assert3.c, function_name: main, line 31
Aborted
此刻就不会在履行接下来的打印句子了。看看咱们自己的完成办法就知道,咱们自己编写的断语能够比直接调用assert宏能够得到更多的信息量,首要是因为咱们自己编写的断语愈加的具有灵敏性,能够依据自己的需要来打印输出不同的信息,一起也能够关于不同类型的过错或许正告信息运用不同的断语,这也是在工程代码中常常运用的做法。假如你在重视代码运转成果的一起也仔细的阅读了我的代码,你会发现其间我在宏界说中运用了一个do{}while(0),运用它有什么优点呢,或许在以上的代码中并没有表现出来,那么咱们看看下面的代码你就知道了。
#include
void print_1(void)
{
printf("print_1\n");
}
void print_2(void)
{
printf("print_2\n");
}
#define printf_value() \
print_1(); \
print_2(); \
int main( void )
{
int i=0;
if(i==1)
printf_value();
return 0;
}
运转成果:
仍是备份一下文章描绘,以防图片翻开失利给读者带来困扰。
print_2
Press any key to continue
看了上面运转成果或许有的读者会很疑问为什么会呈现以上的过错呢?!if句子的条件不满意,那么print_value()函数应该不会被调用啊,怎么会打印呢。假如咱们把上面的printf_value()替换为 print_1(); print_2();,就会很清楚的发现if句子在此的效果仅仅是不调用print_1();,而print_2();在操控之外,所以呈现了上面的成果,有的读者或许会立刻想到咱们加上一个{}不就好了吗,在这里确实是加一个{}就能够了,因为这里是一个特殊情况,没有else句子,假如咱们在以上的宏界说中运用{},参加else句子后再来看看代码。
#include
void print_1(void)
{
printf("print_1\n");
}
void print_2(void)
{
printf("print_2\n");
}
#define printf_value() \
{ \
print_1(); \
print_2();}
int main( void )
{
int i=0;
if(i==1)
printf_value();
else
printf("add else word!!!");
return 0;
}
看似正确的代码,咱们编译就会呈现如下过错:
error C2181: illegal else without matching if
为什么会呈现这样的过错呢?因为咱们编写C言语代码时,在每个句子后边加分号是一种约定俗成的习气,以上代码中咱们在printf_value()句子后边加了一个分号,正是因为这个分号的效果使得else没有与之相对应的if,所以编译犯错。可是假如咱们运用do{}while(0)就不会呈现这些问题,所以咱们在编写代码的时分应该学会在宏界说中运用do{}while(0)。
C言语断语内容的解说到此就该完毕了,上面内容已给出了在C言语编写代码的过程中止语较为具体的运用,其间后边运用咱们自己完成的断语算得上是一个比较经典的断语规划办法了,读者能够在自己今后编写C言语代码的过程中参阅下。因为自己水平有限,博客中的不当或过错之处在所难免,殷切期望读者批评指正。一起也欢迎读者一起讨论相关的内容,假如愿意沟通的话请留下你名贵的定见。