0、引言 我們在嵌入式開發的過程中,經常可以碰到在一些巨集定義或者是代碼段中使用了do {...} while(0)的語句,從語義上理解,do {...} while(0)內的邏輯就只執行一次,並沒有迴圈執行,粗略看來,似乎畫蛇添足了,那麼為什麼還需要在只執行一次的邏輯外面加上一層do {...} w ...
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
更多技術內容和書籍資料獲取,入群技術交流敬請關註“明解嵌入式”
本文來自博客園,作者:Sharemaker,轉載請註明原文鏈接:https://www.cnblogs.com/Sharemaker/p/17142670.html