草庐IT

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

明解嵌入式 2023-03-28 原文

0、引言        

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


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

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

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

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

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

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

1 if(判断条件)
2     REPLACE_FUN();

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

1 if(判断条件)
2    funA();
3 funB();   //此处funB()一定会执行,造成逻辑错误

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

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

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

1 if(判断条件)
2     REPLACE_FUN();
3 //宏展开后为:
4 if(判断条件)
5 {
6     funA();
7     funB();
8 };    //此处替换后多一个分号;导致编译报错

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

 1 define REPLACE_FUN() \
 2         do{ \
 3             funA();\
 4             funB();\
 5           }while(0)\
 6 //宏展开前为:         
 7 if(判断条件)
 8     REPLACE_FUN();
 9 //宏展开后为:
10 if(判断条件)
11      do{
12           funA();
13           funB();
14      }while(0);    //根据判断条件,正确执行了一次逻辑


2、避免goto语句的使用

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

 1 void fun(int a)
 2 {
 3    if(1 == a)
 4    {
 5        ...//todo
 6        goto exit;
 7    }  
 8    if(2 == a)
 9    {
10      ...//todo
11      goto exit;
12    }
13 exit:
14    ...//todo
15    printf("a is error"\n);
16 }

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

 1 int fun(int a)
 2 {
 3    do{
 4        if(1 == a)
 5        {
 6          ...//todo
 7          break;
 8        }  
 9        if(2 == a)
10        {
11          ...//todo
12          break;
13        }
14    }while(0);
15    ...//todo
16    printf("a is error"\n);
17 }

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

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

 1 int a;
 2 char b;
 3 int func()
 4 {
 5     int a = 3;
 6     char b = 5;
 7     do{
 8         int a;
 9         char b;
10         ......//todo
11     }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

更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”

有关你想知道的do{...}while(0)的作用,都在这里了的更多相关文章

  1. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  2. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  3. ruby - 从 Ruby : capturing the output while displaying the output? 运行 shell 命令 - 2

    我有一个问题。我想从另一个ruby​​脚本运行一个ruby​​脚本并捕获它的输出信息,同时让它也输出到屏幕。亚军#!/usr/bin/envrubyprint"Enteryourpassword:"password=gets.chompputs"Hereisyourpassword:#{password}"我运行的脚本文件:开始.rboutput=`runner`putsoutput.match(/Hereisyour(password:.*)/).captures[0].to_s正如您在此处看到的那样,存在问题。在start.rb的第一行,屏幕是空的。我在运行程序中看不到“输入您的密

  4. ruby - EventMachine - 你怎么知道你是否落后了? - 2

    我正在研究使用EventMachine支持的twitter-streamruby​​gem来跟踪和捕获推文。我对整个事件编程有点陌生。我如何判断我在事件循环中所做的任何处理是否导致我落后?有没有简单的检查方法? 最佳答案 您可以通过使用周期性计时器并打印出耗时来确定延迟。如果您使用的是1秒的计时器,您应该已经过了大约1秒,如果它更长,您就知道您正在减慢react器的速度。@last=Time.now.to_fEM.add_periodic_timer(1)doputs"LATENCY:#{Time.now.to_f-@last}"@

  5. ruby-on-rails - "assigns"在 Ruby on Rails 中有什么作用? - 2

    我目前正在尝试学习RubyonRails和测试框架RSpec。assigns在此RSpec测试中做什么?describe"GETindex"doit"assignsallmymodelas@mymodel"domymodel=Factory(:mymodel)get:indexassigns(:mymodels).shouldeq([mymodel])endend 最佳答案 assigns只是检查您在Controller中设置的实例变量的值。这里检查@mymodels。 关于ruby-o

  6. ruby - 你会如何在 Ruby 中表达成语 "with this object, if it exists, do this"? - 2

    在Ruby(尤其是Rails)中,您经常需要检查某物是否存在,然后对其执行操作,例如:if@objects.any?puts"Wehavetheseobjects:"@objects.each{|o|puts"hello:#{o}"end这是最短的,一切都很好,但是如果你有@objects.some_association.something.hit_database.process而不是@objects呢?我将不得不在if表达式中重复两次,如果我不知道实现细节并且方法调用很昂贵怎么办?显而易见的选择是创建一个变量,然后测试它,然后处理它,但是你必须想出一个变量名(呃),它也会在内存中

  7. ruby - 从 sinatra 中的 before do block 返回不同的值 - 2

    有没有办法在sinatra的beforedoblock中停止执行并返回不同的值?beforedo#codeishere#Iwouldliketo'return"Message"'#Iwouldlike"/home"tonotgetcalled.end//restofthecodeget'/home'doend 最佳答案 beforedohalt401,{'Content-Type'=>'text/plain'},'Message!'end如果你愿意,你可以只指定状态,这里有状态、标题和正文的例子

  8. ruby-on-rails - ruby rails : How do you prefix named routes? - 2

    我希望生成一个链接,该链接的前缀附加到命名路由本身。像这样显示路径“/old/recipes”:recipes_path(:prefix=>"old/")#thecorrectwayshouldshow"/old/recipes"我不想动routes.rb文件,而是修改带前缀的命名路由。这可能吗?您将如何正确地做到这一点?编辑:我正在使用Rails3。添加可选前缀的原因是我也想使用普通的recipes_path。所以我想同时使用“/recipes”和“/old/recipes”。 最佳答案 如果您不想触及路由文件,您将会遇到很多麻烦

  9. ruby-on-rails - Rails FasterCSV "unquoted fields do not allow\r or\n" - 2

    我在使用FasterCSV和我的rakedb:seeds迁移时遇到问题。我收到错误:“rake中止!未加引号的字段不允许\r或\n(第2行)”在以下seeds.rb数据上:require'csv'directory="db/init_data/"file_name="gardenzing020812.csv"path_to_file=directory+file_nameputs'LoadingPlantrecords'#Pre-loadallPlantrecordsn=0CSV.foreach(path_to_file)do|row|Plant.create!:name=>row[1

  10. ruby - 字符串文字前面的 * 在 ruby​​ 中有什么作用? - 2

    这段代码似乎创建了一个范围从a到z的数组,但我不明白*的作用。有人可以解释一下吗?[*"a".."z"] 最佳答案 它叫做splatoperator.SplattinganLvalueAmaximumofonelvaluemaybesplattedinwhichcaseitisassignedanArrayconsistingoftheremainingrvaluesthatlackcorrespondinglvalues.Iftherightmostlvalueissplattedthenitconsumesallrvaluesw

随机推荐