c 退出函式 乾貨 函式巨集的三種封裝方式

2021-10-17 08:05:39 字數 3865 閱讀 3155

函式巨集,即包含多條語句的巨集定義,其通常為某一被頻繁呼叫的功能的語句封裝,且不想通過函式方式封裝來降低額外的彈棧壓棧開銷。

函式巨集本質上為巨集,可以直接進行定義,例如:

#define int_swap(a,b) \

int tmp = a;    \

a = b;          \

b = tmp

但上述的巨集具有乙個明顯的缺點:當遇到ifwhile等語句且不使用花括號僅呼叫巨集時,實際作用範圍在巨集的第乙個分號後便結束。即a = bb = tmp均不受控制語句所作用。

因此,在工程中,一般使用三種方式來對函式巨集進行封裝,分別為{}dowhile(0)({})。下文將一一對三種方式進行分析,比較各自的優劣點。

int_swap巨集使用{}封裝後形態如下:

#define int_swap(a,b)\

此時,直接呼叫與在無花括號的控制語句(如ifwhile)中呼叫均能正常執行,例如:

#define int_swap(a,b) \

int main()

但當無花括號的if語句存在其他分支(else ifelse等)如:

if (1)

int_swap(var_a, var_b);

else

printf("hello world!\n");

會發現編譯出錯:

...

/mnt/hgfs/share/pr_c/src/main.c: in function 『main』:

/mnt/hgfs/share/pr_c/src/main.c:18:2: error: 『else』 without a previous 『if』

else

這是因為int_swap(var_a, var_b);最後的;已經把if的作用域終結了,後續的else當然沒有找到與之匹配的if了。

因此,解決方法有兩種,分別為不使用;(port.1)或規定必須使用帶花括號的if(port.2),例如:

/* port.1 */

if (1)

int_swap(var_a, var_b)

else

/* port.2 */

if (1)

else

可見,不使用;的呼叫方式無論從程式閱讀還是使用方法方面都是十分彆扭的;而規定必須使用帶花括號的if的呼叫方式有違常理的,因為巨集函式應該適用於任何語法。

優缺點總結:

int_swap巨集使用dowhile(0)封裝後形態如下:

#define int_swap(a,b)   \

dowhile(0)

dowhile(0)表示只執行一遍{}內的語句,表象來說與{}的功能是一致的。不同的是,dowhile(0)可以提前退出函式巨集、集成為一條語句與強制呼叫時必須使用;

由於dowhile(0)實際為 while 迴圈,因此可以使用關鍵字break提前結束迴圈。利用該特性,可以為函式巨集新增引數檢測。例如:

#define int_swap(a,b)  \

dowhile(0)

由於dowhile(0);實際為一種語法,編譯器會把dowhile(0);認為為一條語句。

因此,dowhile(0)方式的函式巨集可以在無花括號且有分支的if語句中直接呼叫。例如:

#define int_swap(a,b)  \

dowhile(0)

int main()

c 語言規定,dowhile(0)語法必須使用;作為語句結尾。因此不可能存在以下語句的程式出現:

if (1)

int_swap(var_a, var_b)

else

優缺點總結:

({})為 gnu c 擴充套件的語法,非 c 語言的原生語法。

int_swap巨集使用({})封裝後形態如下:

#define int_swap(a,b)   \

()

dowhile(0)相同,({})支援在無花括號且有分支的if語句中直接呼叫。例如:

#define int_swap(a,b)  \

()int main()

dowhile(0)不同的是,({})不能提前退出函式巨集與支援返回值。({})畢竟不是 while 迴圈,不能直接使用break退出函式巨集是比較容易理解。那支援返回值是什麼意思呢?

答案是 c 語言規定({})中的最後一條語句的結果為該雙括號體的返回值。例如:

int main());

printf("a = %d\n", a);      // a = 1000

}

因此,({})可以為函式巨集提供返回值。例如:

#define int_swap(a,b)  \

(     \else    \

\ret;    \

})int main()

可見,此時的int_swap巨集已與函式十分接近。

優缺點總結:

綜上,在{}dowhile(0)({})這三種函式巨集的封裝方式之中,應盡可能不使用{},考慮相容性一般選擇使用dowhile(0),當需要函式巨集返回時可以考慮使用({})或直接定義函式。

c語言、嵌入式中幾個非常實用的巨集技巧

c語言高效程式設計與**優化

函式的寫法 C 建構函式的三種寫法

c 的建構函式的作用 初始化類物件的資料成員。即類的物件被建立的時候,編譯系統對該物件分配記憶體空間,並自動呼叫建構函式,完成類成員的初始化。建構函式的特點 以類名作為函式名,無返回型別。常見的建構函式有三種寫法 c 的建構函式可以有多個,建立物件時編譯器會根據傳入的引數不同呼叫不同的建構函式。如果...

C 函式引數傳遞的三種方式

1.值傳遞 void myswap01 int a,int b 2.位址傳遞 void myswap02 int a,int b 3.引用傳遞 void myswap03 int a,int b int main 結果 值傳遞並不改變實參的大小,位址傳遞和引用傳遞均改變位址大小。原因是什麼?是因為考...

javascript三種函式形式

1.普通函式 function show message show test 定義 同python中函式定義方法一致,關鍵字替換為function 執行 呼叫方法和一般語言中呼叫方法一致,可有返回值 2.匿名函式 setinterval function 1000 a function messag...