【嵌入式C语言】--do{…}while(0)的学习

文章参考:(1条消息) C语言 -- do{…}while(0)的意义和用法_诸葛一帆丶的博客-CSDN博客

在学习STM32H743的代码过程中,经常碰到一些有关do{...}while(0)的宏定义。

如,在学习stm32h7xx_hal_rcc.h文件时,就有相关的宏定义:

#define                            __HAL_RCC_GPIOA_CLK_ENABLE()       do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN);\
                                        UNUSED(tmpreg); \
                                       } while(0)

我们都知道do{...}while{0}循环只执行一次,那么宏定义中使用do{...}while{0}循环意义何在呢?以下为我在学习过程中的一些感悟。仅供参考。

1.将定义的复杂的宏用作一个整体来引用。 

举例,有时候在使用宏定义时,往往需要将多个C语句汇集到一起来定义一个宏。即使用宏定义来定义一个函数,用以实现某种功能,我们姑且称之为宏函数。如x,y比较大小,x大则x,y的值互换。反之x,y的值不互换。对此我们首先想到的宏定义使用方法为:

#define swap(x,y) temp=x; x=y; y=temp;     //定义宏函数。

此时,使用时。

int x=..., y=..., temp;

if(x>y)

        swap(x,y);                     //调用宏函数,调用函数时一般都需要加分号,而宏定义中语句          y=temp;可以利用此处的分号,但因y=temp为有一条C语句,因此按照习惯加了一个分号

else...

编译器进行处理后的代码为:

int x=...;y=...;

if(x>y)

        temp=x;                 

x=y;                        

y=temp;

;                                               //空语句 

else...

这时,编译的结果就和想要的结果完全相反,宏定义中的 x=y; y=temp; 语句是一定执行的,而语句 temp=x; 则是条件执行。这与预期的结果完全不符合。

那么,使用{}在宏定义中,将C语句包裹起来能解决问题吗?

#define swap(x,y) {temp=x; x=y; y=temp;}

此时,使用时。

int x=..., y=..., temp;

if(x>y)

        swap(x,y);

else...

编译器进行处理后的代码为:

int x=..., y=..., temp;

if(x>y)

{

temp=x;                 

x=y;                        

y=temp;   

}

;                                                //空语句   

else...

此时,if条件如果成立的话会先执行swap(x,y)的功能,之后有一条空语句,这会导致else语句没有相应的if语句与之匹配,编译不通过,甚至会发生错误的匹配。

【嵌入式C语言】--do{…}while(0)的学习_第1张图片

总结:究其原因,swap(x,y)函数是由多个语句组成,而宏定义只是将相应的文档进行的替换,而后续的编译规则导致了编译出错。所以思考能否将swap(x,y)中所有的C语句合成一个整体的C语句来进行调用。而do{...}while(0)语句能够合理的解决这一问题。

#define swap      do{\

                           temp=x;\

                           x=y;\

                           y=temp;\

                           }while(0)

此时,使用时:

int x=..., y=..., temp;

if(x>y)

        swap(x,y);

else...

编译器进行处理后的代码为:

int x=..., y=..., temp;

if(x>y)

        do

        {

                temp=x;

                x=y;

                y=temp;

        }while(0);

else...

这样的话就能很好地解决以上遇到的问题。

宏定义用法很灵活,如

#define swap(x,y) temp=x; x=y; y=temp;

int x=..., y=..., temp;

if(x>y)

{

      swap(x,y)          

}

else...

也能实现相关功能,但由于我们常常习惯将swap(x,y)作为一个函数来进行调用,因此一般会在调用之后加分号,这样就可能会出现以上的错误,因此,实际中宏定义的使用很灵活,但是尽量需要一个统一的标准,因此,尽量使用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(0)来进行统一的管理:

int foo()
{
 
    somestruct* ptr = malloc(...);
 
    do{
        dosomething...;
        if(error)
        {
            break;
        }
 
        dosomething...;
        if(error)
        {
            break;
        }
        dosomething...;
    }while(0);
 
    free(ptr);
    return 0;
 
}

改程序中用break语句代替goto语句实现相关功能,结束前的处理工作在while循环之后,能保证一定实现。

3.避免编译器对空的宏定义报错。

内核中由于不同架构的限制,很多时候会用到空宏,在编译的时候,空宏会给出warning,为了避免这样的warning,就可以使用do{}while(0)来定义空宏:

#define EMPTYMICRO do{}while(0)

这部分自己不太懂,参考文章给出的解释。

4.定义一个单独的函数块来实现复杂的操作。

当你的功能很复杂,变量很多你又不愿意增加一个函数的时候,使用do{}while(0);,将你的代码写在里面,里面可以定义变量而不用考虑变量名会同函数之前或者之后的重复。

这个自己并没有遇到过,因此也不太理解。

受作者知识水平限制,文章在所难免会有一些错误,如果有错误欢迎大家提出来,也希望文章能对大家有所帮助。

你可能感兴趣的:(嵌入式,编程语言,stm32)