#define+do{} while(0)+peeror的思考

来源:互联网 发布:通达信的行情软件 编辑:程序博客网 时间:2024/06/06 09:48

今天看到这样一个程序,还挺有意思,感觉里面知识点挺多的,就想着复习复习!!

#define ERR_EXIT(m)   do  {   perror(m);   exit(EXIT_FAILURE);}  while(0) 


1.    perror函数

2.    exit函数以及EXIT_TAIURE

3.    #define 与do{}while()函数的特殊结合




关于perror函数:

头文件:       #include     <stdio.h>(不可以掉了这个文件,因为perror就是在这个文件中)和   #include <stdlib.h>


定义函数

void perror(const char *s);

函数说明

perror( ) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno 的值来决定要输出的字符串。
在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用"某些"函数出错时,该函数已经重新设置了errno的值。perror函数只是将你输入的一些信息和现在的errno所对应的错误一起输出。

范例

#include <stdio.h>
      #include <errno.h>
int main(void)
{
FILE *fp ;
fp = fopen( "/root/noexitfile", "r+" );
if ( NULL == fp )
{
perror("/root/noexitfile");
}
return 0;
}

运行结果

[root@localhost io]# gcc perror.c
[root@localhost io]# ./a.out
/root/noexitfile: No such file or director

关于exit和EXIT_TAIURE

我们在main函数中通常用:return 0;来结束程序,返回一个值,但是这些仅仅是局限于非void的情况下,也就是   void  main()中。但是如果把exit函数用在main函数中,不管main函数的返回值是不是void,exit都是有效,在(int  main()的情况下,返回值等同于return)

 

exit用在子程序中,终结程序,跳回操作系统


只用exit(0)代表程序的正常退出,其余的参数皆表示程序的异常退出


如果main()在一个递归程序中,exit()仍然会终止程序;但return将

控制权移交给递归的前一级,直到最初的那一级,此时return才会终止程序。return和exit()的另一个区别在于,即使在除main()之外的函数中调用exit(),它也将终止程序。

然而对于:EXIT_FALURE(在vc6.0中,它被定义于头文件stdio.h中)

#define  EXIT_FALURE   0

#define  EXIT_SUCESS  1




关于宏中使用do-while函数

一般我们都知道do-while函数基本上都用于循环中,但是在这里为什么要加入do while 0呢,不是也是执行一次吗,放在这里它有什么优势吗???我们知道define函数的简单用法是:定义一个常量,或者一个简单地语句,或者函数,都只是简单地往后面加一个分号(“;”),但是究竟多个顺序执行的语句如何跟define联系起来呢?它们又跟do-while有什么关系呢??


在define中定义一个可以执行多个函数的宏

第一种:

#define Sequence     print1();print2();

然后我们的程序可能这样调用:

Sequence;

 然后它的确是在预编译期就被这样的替换了:

print1();print2();
能达到我们的预期效果:但是如果我们的程序是这样的呢?

if(flag)        Sequence;
它就被替换成为了这样(就不是我们预期的那样了):

if(flag)        print1();printf(2);
也就是说无论flag这个标志值是真的假的,print2()这个函数都会被执行了,
所以这是我们经常犯的错误,也是大家经常犯得错误了

第二种

#define Sequence    {print1();print2();}

可以看到跟第一种的区别仅仅是,将两个函数放在了大括号中,但是,这样真的可以吗??

   Sequence;
这样子也是可以的,就是相当于这样:

 {        print1();        print2();};   

从上面我们可以看到对程序的多个语句加上大括号(并且加上分号),是对整个程序没有影响的

但是,如果是放在if语句中呢?

if(flag)        Sequence();else        printf("123");


可以知道,上面的程序被替换也就成了这样的:

if(flag){         print1();         print2();};else        printf("123");

这时,编译器就会报错,编译错误,大家可以看到这个结构已经不满足if-else结构了

但是,值得注意的是:

单独使用if语句是后面的分号可有可无,就是说:

if(flag) {         print1();         print2();};
等价与:

if(flag) {         print1();         print2();}


第三种:

使用do()-while(0)函数,构造后的宏定义不会受到大括号,分号的影响,基本上总是来按照我们的意愿行事

所以我们会这样写:


#define  Sequence   do  { print1(); print2();}while(0)

现在,该函数的功能等价与前面两着的综合,do能确保大括号里的逻辑被执行,而while(0)确保里面的逻辑被执行一次,即和没有循环是一样的

所以当前这个语句在if中的话:

if (flag)       Sequence;
可以用这个来进行替换:

if (flag)      do  { print1(); print2();}while(0);

同时,也等价与:

if(flag) {         print1();         print2();}

其次,


当然我们这里声明:

#define  Sequence   do  { print1(); print2();}while(0)
上面这个语句和下面这个也是一样的,而且更容易出现
#define  Sequence  \ do                           \ { print1();                \   print2();                \ } while(0)



所以我们才会在Linux内核和其它一些著名的C库中有许多使用do{...}while(0)的宏定义




2,消除goto语句对程序流的统一控制

   有些函数中,在函数return之前,我们经常会进行一些收尾的工作,比如free掉一块函数开始的malloc内存,goto一直都是一个比较简单的方法

int foo(){    somestruct* ptr = malloc(...);     dosomething...;    if(error)    {        goto END;    }     dosomething...;    if(error)    {        goto END;    }    dosomething...; END:    free(ptr);    return 0; }

但是,goto语句不符合软件工程的结构化,致使写的代码可能会难懂,所以这里也就被大部分人不提倡使用,取而代之的是do-while外加break;
int foo(){     somestruct* ptr = malloc(...);     do{        dosomething...;        if(error)        {            break;        }         dosomething...;        if(error)        {            break;        }        dosomething...;    }while(0);     free(ptr);    return 0; }

这里,我们用break代替goto完美的解决了上面的问题!!


3,  避免空宏引起的警告warning,为了避免warning我们采用这样的结构:

#define    do{ }while(0)



参考:百度百科

    http://blog.csdn.net/luoweifu/article/details/38563161

    http://blog.csdn.net/ypist/article/details/7886209
0 0