do{...}while(0)结构的使用

-- TOC --

据牛人说,这种结构定义的macro,是唯一正确的;在某些场景下,这种结构可以避免使用goto,逻辑也非常清晰。

定义do{...}while(0)结构的使用macro

比如:

#define ERRBUF_SIZE 64
#define _PERROR \
    do { \
        char errbuf[ERRBUF_SIZE] = {0}; \
        sprintf(errbuf, "%s:%d", __FILE__, __LINE__); \
        perror(errbuf); \
    } while(0)

_PERROR可以直接被当做一个inline的函数使用(后面带上分号),而且因为用了do {...} while(0)这样的代码结构定义,这个macro在任何C语言结构中,都可以正确地被展开。

这个结构还使用了C语言的一个特性,即在一个{}结构中,可以定义变量,变量名称与外面的变量无关,变量在这个结构结束的时候消失。

只有这种结构的macro function才适用任何C语言结构,保证没错!

代替goto

恰当的使用goto是OK的!但如果实在不想用,可以这样:

int foo(){
    somestruct *ptr = malloc(...);

    do{
        dosomething...;
        if(error)
            break;
        dosomething...;
        if(error)
            break;
        dosomething...;
    }while(0);

    free(ptr);
    return 0;
}

这里将函数主体部分用do{...}while(0)结构包含起来,使用break来代替goto,后续的清理工作在while之后,现在既能达到同样的效果,而且代码的可读性、可维护性都要比上面的goto代码好的多了。

Big Case

下面这个case,将前面说的两点结合了起来,实现了一个LOG macro接口,同时输入到屏幕以及记录日志到文件:

$ cat test.c
#include <stdio.h>
#include <string.h>


#define _LOGFILE  "xyz_log.txt"
#define _LOG(x, ...) \
    do { \
        FILE *fp = fopen(_LOGFILE, "a"); \
        if (fp == NULL) { \
            fprintf(stderr, "open " _LOGFILE " failed.\n"); \
            break; \
        } \
        fprintf(fp, x, ##__VA_ARGS__); \
        fclose(fp); \
        printf(x, ##__VA_ARGS__); \
    } while(0)


int main(void){
    _LOG("abc""123""\n");
    _LOG("int:%d float:%f\n", 1, 2.12345);
    _LOG("do while and break is good %d %f\n", 1, 2.12345);
    return 0;
}
$ gcc -Wall -Wextra test.c -o test
$ ./test
abc123
int:1 float:2.123450
do while and break is good 1 2.123450
$ cat xyz_log.txt 
abc123
int:1 float:2.123450
do while and break is good 1 2.123450

上面这段代码,在使用的时候,还是有不方便的地方,比如文件名的指定,也不支持线程安全。

我后来又写了一个log函数接口,实现可同时输出到屏幕和文件,不再使用macro的方式,可以灵活指定文件,以及线程安全:同时输出到屏幕和文件的log接口

作为函数内部的一个独立代码块

可以定义一套新的变量,用break可以实现跳出,通过赋值可以实现传出参数。但为什么要这么做呢?

本文链接:https://cs.pynote.net/sf/c/202112261/

-- EOF --

-- MORE --