你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

几种函数宏的封装方式

[复制链接]
gaosmile 发布时间:2020-12-14 22:47
1、函数宏介绍函数宏,即包含多条语句的宏定义,其通常为某一被频繁调用的功能的语句封装,且不想通过函数方式封装来降低额外的弹栈压栈开销。函数宏本质上为宏,可以直接进行定义,例如:#define INT_SWAP(a,b) \' Z9 ~# ^6 L% w
    int tmp = a;    \
# A  g0 M) b3 z# u5 [$ H  ~) A    a = b;          \$ h- Z" v7 @1 H5 q5 y( U/ z
    b = tmp
* e9 Y$ s( L5 O4 T/ f
但上述的宏具有一个明显的缺点:当遇到 if、while 等语句且不使用花括号仅调用宏时,实际作用范围在宏的第一个分号后便结束。即 a = b 和 b = tmp 均不受控制语句所作用。因此,在工程中,一般使用三种方式来对函数宏进行封装,分别为{}、do{...}while(0) 和 ({})。下文将一一对三种方式进行分析,比较各自的优劣点。
* I5 g' D1 }* p
3 ^. D9 o; T; B( A5 ?2 ]: f! F
2、{} 方式
INT_SWAP 宏使用 {} 封装后形态如下:
#define INT_SWAP(a,b)\
2 d' y5 v- M) W' I4 g1 C3 e9 W; X{                   \
) F5 Q+ K$ v6 S! q6 Q4 g0 K    int tmp = a;    \+ _: o7 L& j8 |$ d
    a = b;          \
* G9 i1 ]- Z6 K4 P4 s$ V    b = tmp;        \* |( d; `/ H' t  j& j1 p# V
}
6 g6 y  |- @8 t  L. C% N* }) Y) L
此时,直接调用与在无花括号的控制语句(如 if、while)中调用均能正常运行,例如:
#define INT_SWAP(a,b) \2 r; [9 |7 _9 Z* }6 \* m# U; {
{                   \+ q7 s" o8 q8 d: Q) z( R* U
    int tmp = a;    \
5 }: v6 k" h* a' j  R2 K& D    a = b;          \
2 f4 t# q; Q8 @: {* f    b = tmp;        \' s: ~. S7 q" k' f7 C) g0 T
}
) n# D) e) Q* O" x" @1 P8 v" g  F3 l1 l7 ~
int main()  _2 h+ l% s7 A9 Y8 K# C8 Q2 ^
{5 s3 U: z$ B. K* _  l9 A
int var_a = 1;$ y2 P+ Z4 O! h, {0 t
int var_b = 2;/ t" k8 L( h# r9 A3 l
0 H" c* c9 D, x% ^$ m" a
INT_SWAP(var_a, var_b);( G; P" _$ \( w
printf("var_a = %d, var_b = %d\n", var_a, var_b);   // var_a = 2, var_b = 1
& U" J( ^- x5 P- S) F) Z . l$ y( k2 b0 v. k3 P
if (1)
7 n3 T+ K4 h4 R/ p* r, _$ }    INT_SWAP(var_a, var_b);+ h8 q# `* ~$ R( _" M8 B  S% z' }$ [6 R
printf("var_a = %d, var_b = %d\n", var_a, var_b);   // var_a = 1, var_b = 2( e5 K+ k$ W9 p1 x2 t
}- z; g2 h. o, G9 O9 P7 Y
但当无花括号的 if 语句存在其他分支(else if、else 等)如:
if (1)
1 {3 X8 H* r7 Y7 P9 Q1 W   INT_SWAP(var_a, var_b);
  E, j! q1 j' p/ C1 Eelse
: f2 \9 L. ~: X  o1 | printf("hello world!\n");5 \/ D, S5 Y& {) W
会发现编译出错:
...
9 h/ K# Z# t' E2 u. E% f/mnt/hgfs/share/pr_c/src/main.c: In function ‘main’:
. l9 p4 \1 {: a) Y/mnt/hgfs/share/pr_c/src/main.c:18:2: error: ‘else’ without a previous ‘if’
( d+ V( [1 l% g' ~& b( W( T' Q3 T  else
) j! ^! Q8 s) x6 f
这是因为 INT_SWAP(var_a, var_b); 最后的 ; 已经把 if 的作用域终结了,后续的else 当然没有找到与之匹配的 if 了。因此,解决方法有两种,分别为不使用 ;(port.1)或规定必须使用带花括号的if(port.2),例如:/* port.1 */0 w3 o0 V* e$ R9 \- L( V
if (1)
) @8 j7 G- e. C: K2 v* E   INT_SWAP(var_a, var_b)
' w9 }& V, R7 D3 [: ?& y- pelse
' D7 R8 d2 x' C4 u' f{* i. o( k; f5 J
    printf("hello world!\n");. p) y+ t  n' P6 O2 E4 H0 q  V
}) D1 @) [2 z& c* F

6 u4 l5 L6 h2 Q; t/* port.2 */+ I  \; l) d5 o' Z5 T
if (1)( S. y' d3 \1 z
{# i% X* e- P2 R' y7 ]' P
   INT_SWAP(var_a, var_b);
& V. S# i: B3 K}
1 \0 I  H: {1 d3 v, u9 Ielse( Y% Y! B5 ^7 a" {2 \- B! B7 {
{2 g. `, {0 W& d  l8 W
    printf("hello world!\n");
) ^. F2 _. }8 F, y& t6 x" H$ W}4 C3 F* b+ ?8 s& j, R
可见,不使用 ; 的调用方式无论从程序阅读还是使用方法方面都是十分别扭的;而规定必须使用带花括号的 if 的调用方式有违常理的,因为宏函数应该适用于任何语法。
优缺点总结:
  • 优点:简单粗暴。
  • 缺点:不能在无花括号且有分支的 if 语句中直接调用;能够不带 ; 直接调用。
    # J7 E1 C' S; B
3、do{…}while(0) 方式
INT_SWAP 宏使用 do{...}while(0) 封装后形态如下:
#define INT_SWAP(a,b)   \8 W, |6 l' h8 _& A
do{                     \
- a+ {& G. ^/ G% m9 C3 L    int tmp = a;        \  ~. H  M1 y" x
    a = b;              \
6 ~* y* h/ H+ N# ~5 M; r' L  Z    b = tmp;            \
, T. O8 d: c1 u9 Q! b' E8 O}while(0)9 b: T( D1 ]0 h0 D
do{...}while(0) 表示只执行一遍 {} 内的语句,表象来说与 {} 的功能是一致的。不同的是,do{...}while(0) 可以提前退出函数宏、整合为一条语句与强制调用时必须使用 ;。
由于 do{...}while(0) 实际为 while 循环,因此可以使用关键字 break 提前结束循环。利用该特性,可以为函数宏添加参数检测。例如:
#define INT_SWAP(a,b)  \4 {6 J) g( A% V% U3 W+ V5 i- }
do{                 \0 a! ?2 r  R2 \/ E: x' ]1 Q
if (a < 0 || b < 0) \
) D, ~6 ], Y. `7 Y  break;   \
0 w: N7 H4 r* l4 y    int tmp = a;     \
/ A  I5 H) E) Y) G" R2 t    a = b;           \$ i" D2 q5 s8 P8 s1 ]# ~7 H4 U/ @
    b = tmp;         \8 r2 t- H) z6 `- r% @- R- Y8 }
}while(0)
# ], [" b" O- v+ d1 Q' v: S
由于 do{...}while(0); 实际为一种语法,编译器会把 do{...}while(0); 认为为一条语句。
因此,do{...}while(0) 方式的函数宏可以在无花括号且有分支的 if 语句中直接调用。例如:
#define INT_SWAP(a,b)  \; r* ~) M: l3 \. \
do{                 \, s4 X! G+ F& v
if (a < 0 || b < 0) \+ |' V' u( |" x2 q- f3 f' }! G
  break;   \- b* G. I% a) o
    int tmp = a;     \
5 X+ H/ s6 Z7 k& V2 _    a = b;           \
8 ?. c+ t+ U1 R4 J    b = tmp;         \- y4 B* p( Y( r5 e# O. [- v
}while(0)
2 S6 Q" H+ I8 g: O) P0 [
' Y  ~7 X6 c0 w5 |/ iint main()4 K5 K5 N; @+ F' h+ W4 \
{
- r1 g+ v+ n% e" v! U int var_a = 1;# |, G, ~( p0 |( c
int var_b = 2;* @6 X1 k( m3 a* R, ?
- l7 H+ j& V; V( I
if (1)* s, y: f, w; C
    INT_SWAP(var_a, var_b);  S" ~$ w* j! V; }* ~1 {! r$ V6 {
else
% }( q% [$ ?+ j6 h! D  printf("hello world!\n");
2 F0 s# N8 L  N  }+ T8 n' n printf("var_a = %d, var_b = %d\n", var_a, var_b); // var_a = 2, var_b = 1
3 ^1 D# G- o% w
# d0 x! O1 [: x9 O1 r" e return 0;" s+ H0 ?+ h3 T2 l- o% j, U0 q$ B+ @
}
' G4 ?6 B& Z/ y% j' u( e8 M/ j
) r* E  _0 W& Y, o
C 语言规定,do{...}while(0) 语法必须使用 ; 作为语句结尾。因此不可能存在以下语句的程序出现:
if (1): X0 `2 q3 Q' ~0 S
   INT_SWAP(var_a, var_b)! a# A' d% }2 I) v& k
else# g6 F6 z! J% E9 Z5 \
{
4 }  N6 o/ e+ u" u% B printf("hello world!\n"); " o9 q2 q8 o1 P* O# R
}
( x# l) `4 L6 {( a! F9 H& o4 V$ w+ y9 b8 V& m  R2 [
优缺点总结:
  • 优点:支持在无花括号且有分支的 if 语句中直接调用;支持提前退出函数宏;强制调用时必须使用 ;。
  • 缺点:无返回值,不能作为表达式的右值使用。
    7 _0 e+ a! m! r" s
4、 ({}) 方式
({}) 为 GNU C 扩展的语法,非 C 语言的原生语法。
INT_SWAP 宏使用 ({}) 封装后形态如下:
#define INT_SWAP(a,b)   \. m5 S3 B% v. k% c# O) t  k8 i
({                      \
# L& l7 {% d( M9 P9 e0 X    int tmp = a;        \
. ?  V' R3 o6 M, ^    a = b;              \* [( b3 n0 Z0 x
    b = tmp;            \
& d' o- h1 J; b6 r8 T})
4 A  H. B( Q; x, R# [3 _7 @4 F/ i0 R: v% k4 ^
与 do{...}while(0) 相同,({}) 支持在无花括号且有分支的 if 语句中直接调用。例如:
#define INT_SWAP(a,b)  \' J3 C8 w4 b& f/ ^, Z" @# u- u
({                 \
" K+ a7 z6 w0 b' G  {- @ int tmp = a;    \4 \5 r5 D  x; Z1 S' C
a = b;          \- b+ Z) a3 O8 O; G5 \7 z5 H$ |
b = tmp;        \' r$ k6 ?" r% k' c6 Q
})
0 K1 z+ Q( l; m  |  ?& u' z
1 g. K8 Q$ k6 J& F* Bint main()
/ {/ y' K; p$ b  ]9 o2 w& t* F{- k4 D, j# I. L! f
int var_a = 1;7 @4 n9 U/ g9 `2 ?3 {
int var_b = 2;8 n* Q: S" [2 u5 W; V
. U0 ?+ z" w% U; @" p* \7 K
if (1)
. W1 N9 A: F, A5 }# Y    INT_SWAP(var_a, var_b);
9 n' x  N  g8 p  S else
$ z* Q3 }" F- `1 [# A  printf("hello world!\n");
" y( S! ~( z) P/ |, B' ?1 K printf("var_a = %d, var_b = %d\n", var_a, var_b); // var_a = 2, var_b = 1
& r/ ?' H, r2 \/ T# l1 I2 i 9 ?) Z" t7 @/ M9 c/ @
return 0;
+ c( e0 Q3 S- w; o* T: j}
' l( C; I8 j& H7 J/ b- S9 \% }3 [! S! L$ Z+ p4 `. t5 \" }
与 do{...}while(0) 不同的是,({}) 不能提前退出函数宏与支持返回值。({}) 毕竟不是 while 循环,不能直接使用 break退出函数宏是比较容易理解。那支持返回值是什么意思呢?
答案是 C 语言规定 ({}) 中的最后一条语句的结果为该双括号体的返回值。例如:
int main()" T( n$ i8 m% G! d1 o+ r7 Q6 l
{% a2 H/ }2 f0 q
int a = ({( W8 P. k- M9 h6 e- n5 T; z- F
  10;
4 C" i' J: k2 X4 N) f* ?' ]  1000;
3 U' b! O8 g& B( k% `' V });6 J& g8 x6 \/ o4 Z
printf("a = %d\n", a);      // a = 1000
* y' i  t+ b; |0 |$ V+ A; n}
: a2 b. w; \: f9 S+ ]* g; S" U- g0 l5 y0 B! m
因此,({}) 可以为函数宏提供返回值。例如:
#define INT_SWAP(a,b)  \
0 R/ D6 b2 u$ `8 y({                 \
' R6 p1 B% B7 z8 N0 \" _% e. M int ret = 0;  \
8 s" G. g9 C: B, i( x7 b, U6 M if (a < 0 || b < 0) \4 b- E) [: i% H4 K4 K! N( r) v2 {
{     \/ R1 J4 O; ^  `; j% L7 _& q0 Z
  ret = -1;  \
' `* v5 U- {( ^$ W6 n# Q }     \
* G; O1 M" Q6 T else    \" R9 P. i8 [( j% j. _6 D
{     \% ^$ H' H/ A) N0 K. Q/ K5 A' S; e! g; ?
  int tmp = a;    \
9 x: J" K$ L2 ?  a = b;          \+ t. b+ i3 I7 O! q6 Y
  b = tmp;        \
/ H, p3 m( M9 Q* T$ r) m) ^9 j }     \
' B* G" b; b, x8 y# s ret;    \
) i% s: o: Y7 |: o  @}): N" V' L. c3 r3 X! K* t" ?$ I
) q7 `$ `* `, B7 h# Y. g
int main()
) X3 ~5 Z% b3 ~( @% G{
- I' r+ ~: W" Z$ |* n2 e7 X int var_a = 1;
& o8 y: c% y4 C( O2 i int var_b = 2;# W* q" `8 o2 Q5 s* b' l' z) s

1 x4 [! x$ \( R% R7 U if (INT_SWAP(var_a, var_b) != -1)
# c9 c2 Z# `; l2 D9 s7 `6 v  printf("swap success !!\n");     // swap success !!! G2 L) D; o8 w+ C- y
else
' G6 h) ]" a! g( U1 R0 t  printf("swap fail !!\n"); . N9 j* s1 F3 y: l- u
printf("var_a = %d, var_b = %d\n", var_a, var_b); // var_a = 2, var_b = 1
' }. z- D2 q$ \5 R( [4 ]+ V, G ; l6 {, W/ @0 U) p. w  O- n2 g% r
return 0;5 N6 q6 c! u! D& B) y2 w9 {
}
0 D! M% Y; e0 ^) Y1 l) J" u; K5 S
可见,此时的 INT_SWAP 宏已与函数十分接近。
优缺点总结:
  • 优点:支持在无花括号且有分支的 if 语句中直接调用;有返回值,支持作为表达式的右值。
  • 缺点:不支持提前退出函数宏;非 C 的原生语法,编译器可能不支持。
    - I0 ^# D$ l3 d5 a: o/ z& A
5、 总结
综上,在 {}、do{...}while(0) 和 ({}) 这三种函数宏的封装方式之中,应尽可能不使用 {},考虑兼容性一般选择使用 do{...}while(0),当需要函数宏返回时可以考虑使用 ({}) 或直接定义函数。

% J3 T3 d( g* p! o7 _1 y  ?; b  o8 {) F9 ]* ~6 M

& x! y8 z0 ^0 n; N
赞 收藏 评论0 发布时间:2020-12-14 22:47

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版