你想知道的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()來替換。
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)的作用,都在这里了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linq下有一个非常实用的SelectM
- 下一篇: 005_awk案例实战