do{}while(0)只執行一次無意義?你可能真的沒理解!

2020-12-11 電子工程專輯

do{conditional code}while(condition)結構

流程圖如下:一般結構如以下代碼

do
{
  //循環體
}
while (條件表達式);

do while/while do的區別

意思是先幹了再說!!

意思是先看看能不能幹!

初見do{...}while(0)

linux內核和其他一些開源的代碼中,經常會遇到這樣的代碼:

do{
 ...
}while(0)

這樣的代碼一看就不是一個循環,do..while表面上在這裡一點意義都沒有,只執行一次而已,那麼為什麼要這麼用呢?

總結了7種妙處

實際上,do{...}while(0)的作用可不止一點點,下面我列舉了一些。

  1. 有時候只是為了代碼分塊,比僅僅使用{}更直觀些。例如在 cocos2d-x代碼中

do
{
    CCImage* pImage = new CCImage();
    CC_BREAK_IF(NULL == pImage);
    bRet = pImage->initWithString(text, (int)dimensions.width, (int)dimensions.height, eAlign, fontName, (int)fontSize);
    CC_BREAK_IF(!bRet);
    bRet = initWithImage(pImage);
    CC_SAFE_RELEASE(pImage);
while (0);

  1. 為了宏展開的時候不會出錯。如果直接放在花括號裡會出錯的

舉例來說,假設你需要定義這樣一個宏:

#define DOSOMETHING() action1(); action2();

這個宏的本意是,當執行DOSOMETHING()時,action1(),action2()都會被調用。如果有判斷,再執行這個宏的話,如下:

if(NULL == pPointer)
  DOSOMETHING();
else
  ...

這樣宏在預處理的時候會直接被展開,放在花括號裡,那麼實際上寫的代碼如下:

if(NULL == pPointer)
    action1();
action2();
else
  ...

這展開存在兩個問題:

  • 因為if分支後面有兩個語句,導致else分支沒有對應的if,編譯失敗。
  • 假設沒有else分支,則DOSOMETHING中的第二個語句無論if測試是否通過,都會執行。

那麼僅僅使用{}把action1()、action2()包起來行麼?比如:

#define DOSOMETHING() { action1(); action2(); }

我們在寫代碼的時候都習慣在語句右面加上分號,如果在宏中使用{},代碼編譯展開後宏就相當於這樣寫了:{...};,展開後如下:

if(NULL == pPointer)
{
    action1();
    action2();
};
else
  ...

這段代碼中大括號後多了一個分號,如果有else,那麼else又沒有對應的if了,編譯出錯。

那麼辦法來了

如果我們使用do{...}while(0)來定義宏,即:

#define DOSOMETHING() \
        do{ \
          action1();\
          action2();\
        }while(0)\

宏被展開後,上面的調用語句會保留初始的語義,同時絕大部分編譯器都能夠識別do{...}while(0)這種無用的循環並進行優化,不會導致性能優化的降低。

小結

在Linux內核和驅動代碼還有cocos2d-x中,很多宏實現都使用do{...}while(0)來包裹他們的邏輯,Google的Robert Love先前從事Linux內核開發)給我們解答如下:

讓你定義的宏總是以相同的方式工作,不管在調用代碼中怎麼使用分號和大括號,而該宏總能確保其行為是一致的。

  1. 當你執行一段代碼到一半,想跳過剩下的一半的時候,如果你正處於 do{...}while(0)循環中,則能用break達到這個目的。

do
{
  執行.
  再執行…
  if (如果有什麼條件滿足)
  {
    我想跳到另外一段代碼了,剩下的不執行了,可是不建議用goto語句,怎麼辦呢?
     break;/*搞定*/
  }
  我有可能被執行.
}while(false)

舉個例子如下

do
{
  if(!a) break;
  //do something here
  if(!b) break;
  //do another thing here   
}while(0);

  1. 變形的goto,有些公司不讓用goto。在一些函數中,需要實現條件轉移,或者構成循環,跳出循環體,使用goto總是一種簡單的方法,例如:

#include <stdio.h>
#include <stdlib.h>
int main()
{
   char *str;
 
   /* 最初的內存分配 */
   str = (char *) malloc(15);
   if(str != NULL)
     goto loop;
 
   printf("hello world\n");
 
loop:
   printf("malloc success\n");

   return(0);
}

但由於goto不符合軟體工程的結構化,而且有可能使得代碼難懂,所以很多人都不倡導使用,這個時候我們可以使用do{...}while(0)來做同樣的事情:

#include <stdio.h>
#include <stdlib.h>
int main()
{
  do{
      char *str;

      /* 最初的內存分配 */
      str = (char *) malloc(15);
      if(str != NULL)
       break;

      printf("hello world\n");
  }while(0);

   printf("malloc success\n");

   return(0);
}

這裡將函數主體部分使用do{...}while(0)包含起來,使用break來代替goto,後續的清理工作在while之後,現在既能達到同樣的效果,而且代碼的可讀性、可維護性都要比上面的goto代碼好的多了。

int a;
a = 10;
int b;
b = 20;

這種代碼在只支持c89的編譯器上是編譯不過去的,比如ADS 2.0。

int a;
a = 10;
do
{
   int b;
   b = 20;
}while(0);

  1. 避免由宏引起的警告 內核中由於不同架構的限制,很多時候會用到空宏。在編譯的時候,這些空宏會給出警告,為了避免這樣的warning,我們可以使用 do{...}while(0)來定義空宏:

#define DOSOMETHING() do{}while(0)

如果你有一個複雜的函數,變量很多,而且你不想要增加新的函數,可以使用do{...}while(0),將你的代碼寫在裡面,裡面可以定義變量而不用考慮變量名會同函數之前或者之後的重複,例如

int key;
string value;
int func()
{
    int key = GetKey();
    string value = GetValue();
    dosomething for key,value;
    do{
        int key;string value;
        dosomething for this key,value;
    }while(0);    
}

但是為了代碼的可讀性,儘量聲明不同的變量名,以便於後續開發人員欣賞

相關焦點

  • do{}while(0)只執行一次無意義?你可能真的沒理解
    一般結構如以下代碼 do while/while do的區別 do while() while() do 意思是先看看能不能幹!
  • do...while循環
    2、       執行流程do…while語句是先執行循環體語句,再判斷<布爾表達式>的值,如果值為true,執行下一次循環;否則,結束循環。3、改寫上篇例,計算1~10的累加和。因為while語句是先判斷再執行循環,故其最少可能執行0次。如果第一次判斷<布爾表達式>的值即為false,while循環體一次也不執行。而do…while語句是先執行循環再判斷,故其最少的執行次數為1。如果如果第一次判斷<布爾表達式>的值即為false,do已經執行了一次循環體語句了。
  • java中if,while,do-while三種循環的區別
    各位小夥伴們,大家好,這次小編要介紹的是Java當中if,while,do-while三種循環的區別三種循環的區別,在之前的文章中,這三種循環小編都有介紹過,就不多說了。現在我們主要是看這三種循環的區別。
  • 你只用do-while來實現循環?太浪費了!
    地球人都知道,do-while語句是C/C++中的一個循環語句,特點是:至少執行一次循環體;在循環的尾部進行結束條件的判斷。其實do-while還可以用在其他一些場合中,非常巧妙的處理你的一些難題,比如:在宏定義中寫複雜的語句;在函數體中中止代碼段的處理。好像有點抽象,那我們就來具體一些,通過代碼來聊聊這些用法。
  • C語言while語句與do-while語句
    一、while語句1.一般形式while(表達式)語句;(1)表達式代表循環條件(2)語句是循環體2.執行流程當循環條件成立的情況下重複執行循環體語句,循環條件不成立時循環結束3.注意的問題(1)while關鍵字後邊的表達式通常是關係表達式或者邏輯表達式(2)如果循環體語句不止一條,
  • 跟我學java編程—深入理解do-while循環語句的用法
    do-while循環與while循環的不同在於:它先執行循環體中的語句,然後再判斷條件是否為真。如果為真則繼續循環,如果為假,則終止循環。因此,do-while循環至少要執行一次循環語句。同樣當有許多語句參加循環時,要用「{」和「}」把它們括起來。
  • do……while循環
    JavaScript循環 - do…whiledo…while的語法格式執行過程:先執行一次do裡面的循環語句,再判斷是否滿足while中的表達式,如果滿足繼續執行do,依次循環,直到不滿足while中的表達式為止。
  • do…while循環
    do…while的語法格式 執行過程:先執行一次do裡面的循環語句,再判斷是否滿足while中的表達式,如果滿足繼續執行do,依次循環,直到不滿足while中的表達式為止。
  • 在C#中,do……while與while都是循環語句,使用時如何選擇呢?
    基本概念在C#中,共有4個與循環相關的語句:for、foreach、while、do…while,今天主要來學習do…while循環,前3個循環已詳細講解過了,可以對比一下,看這4個循環有什麼區別。do…while與while語句相似,它的判斷條件在循環後,do…while稱為後測試循環。
  • 小開帶你學C++|Lesson 8 do-while,二維數組和循環嵌套
    這次我們來學習do-while循環和循環嵌套。先來看do-while循環的寫法:int c=1,a=10;do{ c++;a--;}while(a>=0);很顯然,do裡的內容就是符合while條件的時候的內容。唯一的區別是,while循環是先判斷條件符不符合,然後在執行命令。
  • 3.2.2 JavaScript中do-while循環語句的使用
    JavaScript中的do-while循環語句也稱為後測試循環語句,它先執行一次,然後再測試循環條件是否成立,如果成立則循環,如果不成立則結束循環。其語法格式如下:do{語句組} while(表達式);參數說明:1)語句組:用來指定需要重複執行的語句,即循環體,可以是一條或多條語句。
  • Linux shell awk 流程控制語句(if,for,while,do)詳細介紹
    條件判斷語句(if)if(表達式) #if ( Variable in Array )語句1else語句2格式中"語句1"可以是多個語句,如果你為了方便Unix awk判斷也方便你自已閱讀,你最好將多個語句用{}括起來。
  • 用實際案例 帶你形象理解for&while循環語句
    那麼除了 while 我們還有 do...while 循環語句這個語句和 while 的用法很像的>語法格式:do{執行語句;}while(條件表達式);值的注意的是:while 是先檢測是否滿足條件,再執行循環語句。
  • C語言while語句
    while語句的意思是:先計算表達式的值,當值為真(非0)時, 執行循環體語句;執行完循環體語句,再次計算表達式的值,如果為真,繼續執行循環體……這個過程會一直重複,直到表達式的值為假(0),就退出循環,執行後面的語句。
  • 為什麼要使用do{}while(0...
    使用 do{}while(0)包裹代碼儘管C語言中的「函數式宏定義」和真正的函數相比有一些缺點,但只要小心使用還是會顯著提高代碼的執行效率的,畢竟省去了分配和釋放棧幀、傳參、傳返回值等一系列工作。正因為如此,Linux 內核中有相當多的方法是使用 define 宏定義實現的,並且,在內核C語言代碼中,「函數式宏定義」經常藉助 do{}while(0) 實現,例如:# define spin_unlock(lock) \do {\ __raw_spin_unlock(&(lock)-&
  • 【連載】(循環執行語句while和if)樂創DIY C語言講義​——3.8節(3)
    C語言中實現循環語句結構的方式有三種,第一種為「for」循環,第二種為「while」循環,第三種為「do……while」循環。不管是那種循環,其循環的方式都是類似的,即在某一條件成立的情況下(為True,或者非0),某一範圍內的某條或某幾條語句就會被一次循環執行,循環執行語句的示意如圖3-8-7所示。
  • while、for、try except語句中的else
    else:    doAnthorThingsif condition1:    do1elif condition2:    do2else:    doOtherThings這兩個用法理解起來沒有問題。
  • 流程控制之循環語句while循環語句
    while循環語句while語句也稱條件判斷語句,它的循環方式為利用一個條件來控制是否要繼續反覆執行這個語句。do…while循環語句do…while循環語句與while循環語句類似。它們之間的區別是while語句為先判斷條件是否成立再執行循環體,而do…while循環語句則先執行一次循環後,再判斷條件是否成立。也就是說do…while循環語句中大括號中的程序段至少要被執行一次。
  • Java循環結構,do-while循環的簡單介紹
    各位小夥伴們大家好,這次小編要介紹的是Java的循環結構裡面的do-while循環,之前小編已經介紹了if還有while循環。現在,我們來了解一下do-while循環的結構吧,主要有兩種結構,一種是標準結構,還有一種是擴展結構。