你想知道的do{...}while(0)的作用,都在这里了

0、引言

我们在嵌入式开发的过程中,经常可以碰到在一些宏定义或者是代码段中使用了do {...} while(0)的语句,从语义上理解,do {...} while(0)内的逻辑就只执行一次,并没有循环执行,粗略看来,似乎画蛇添足了,那么为什么还需要在只执行一次的逻辑外面加上一层do {...} while(0)语句呢?实际上,在这些逻辑中使用do {...} while(0)的作用远大于美化你的代码,下面就来看看实际的使用场景。


1、用于定义一个作用域,避免替换的时候出错

我们都知道,在程序中如果一些常量参数或者代码语句反复出现,就可以使用宏定义来替代。预处理阶段,对程序中所有出现的“宏名”,预处理器都会用宏定义中的字符串替代,这称为“宏替换”或“宏展开”。

这样做可提高程序的通用性和易读性,减少不一致性,一个较好的宏名可以更好的让读者理解常量参数的含义;同时程序易于修改,我们仅需要改变一个宏定义,就可以改变整个程序中出现的所有该常量或者语句。

但是有时可能程序代码段中,出现多条语句重复连续的使用,这样我们就可以尝试使用一个复杂的宏来替换。你有可能会这样定义:

1#define REPLACE_FUN() funA(); funB()

  本意是在程序中当出现funA()和funB()多条语句连续使用时,使用REPLACE_FUN()来替换。

1if(判断条件)

2 REPLACE_FUN();

但是实际上在预处理的时候,宏展开替换后变成了:

1if(判断条件)

2 funA();

3 funB(); //此处funB()一定会执行,造成逻辑错误

可以看出,funB()不会按照判断条件才去执行。而是变成了一条独立的语句,而如果在宏中使用括号:

1#define REPLACE_FUN() {funA(); funB();}

我们一般的代码习惯都会在语句的末尾加上分号,因此也会出错:

1if(判断条件)

2 REPLACE_FUN();

3//宏展开后为:4if(判断条件)

5{

6 funA();

7 funB();

8 }; //此处替换后多一个分号;导致编译报错

因此,针对这种多条重复语句的连续使用,如果想用宏替换实现这个作用域的功能,就可以考虑使用do {...} while(0)语句:

1define REPLACE_FUN() \

2do{ \

3 funA();\

4 funB();\

5 }while(0)\

6//宏展开前为: 7if(判断条件)

8 REPLACE_FUN();

9//宏展开后为:10if(判断条件)

11do{

12 funA();

13 funB();

14 }while(0); //根据判断条件,正确执行了一次逻辑


2、避免goto语句的使用

goto语句也称为无条件转移语句,使用后可以从多重循环或者多个判断中直接跳出。对于如下例子:

1void fun(int a)

2{

3if(1 == a)

4 {

5 ...//todo 6goto exit;

7 }

8if(2 == a)

9 {

10 ...//todo11goto exit;

12 }

13exit:

14 ...//todo15 printf("a is error"\n);

16 }

但是为了程序结构的清晰,还是要尽量限制goto语句的使用,我们可以使用do {...} while(0)结构配合break跳出单层的循环的方法来替代这种goto的用法。

1int fun(int a)

2{

3do{

4if(1 == a)

5 {

6 ...//todo 7break;

8 }

9if(2 == a)

10 {

11 ...//todo12break;

13 }

14 }while(0);

15 ...//todo16 printf("a is error"\n);

17 }


3、定义一个单独的函数块来实现复杂的操作

当某个函数程序功能较为复杂,在该函数的代码段中如果不再单独定义一个函数实现部分逻辑,可以使用do {...} while(0)作为一个代码块,将想要实现的逻辑放在do {...} while(0)中,同时在该在do {...} while(0)代码块中定义的变量,可以不用考虑和函数之前或者之后的变量名重复冲突的问题。但是为了代码的易读性,还是尽量声明不同的变量名。

1int a;

2char b;

3int func()

4{

5int a = 3;

6char b = 5;

7do{

8int a;

9char b;

10 ......//todo11 }while(0);

12 }


4、避免空宏的警告

有的时候,程序为了不同的平台移植或者不同架构的限制,很多时候会先定义空宏,后续再根据实际的需要看是否定义具体内容。但是在编译的时候,这些空宏可能会给出warning,为了避免这样的warning,我们可以使用do{...}while(0)来定义空宏,这种情况不太常见,因为有很多编译器已经支持空宏。

1//空宏2#define EMPTY_FUN

3//增加do{...}while(0)来定义空宏4#define EMPTY_FUN do{}while(0) //避免了可能的编译warning

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盈梓的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值