Detailed explanation of three encapsulation methods of function macros in C language

 

Table of contents

​edit

1. Introduction to function macros

3. do{...}while(0) method

4. ({}) method

5. Summary


1. Introduction to function macros

A function macro, that is, a macro definition containing multiple statements, is usually a statement package for a frequently called function, and does not want to use function package to reduce the additional overhead of popping and pushing the stack.

Function macros are essentially macros and can be defined directly, for example:

#define INT_SWAP(a,b) \
    int tmp = a;    \
    a = b;          \
    b = tmp

But the above-mentioned macro has an obvious shortcoming: when a  if, while etc. statement is encountered and the macro is called without curly braces, the actual scope of action ends after the first semicolon of the macro. Namely  a = b and  b = tmp are not affected by control statements.

Therefore, in engineering, three methods are generally used to encapsulate function macros, namely,  {}, do{...}while(0) and  ({}). The following will analyze the three methods one by one, and compare the advantages and disadvantages of each.

                                                      2.  {} way

INT_SWAP After the macro is used and  {} encapsulated, the form is as follows:

#define INT_SWAP(a,b)\
{                   \
    int tmp = a;    \
    a = b;          \
    b = tmp;        \
}

At this point, both direct calls and calls in control statements without curly braces (such as  if, ) can work normally, for example:while

#define INT_SWAP(a,b) \
{                   \
    int tmp = a;    \
    a = b;          \
    b = tmp;        \
}

int main()
{
 int var_a = 1;
 int var_b = 2;

 INT_SWAP(var_a, var_b);
 printf("var_a = %d, var_b = %d\n", var_a, var_b);   // var_a = 2, var_b = 1
 
 if (1)
    INT_SWAP(var_a, var_b);
 printf("var_a = %d, var_b = %d\n", var_a, var_b);   // var_a = 1, var_b = 2
}

if But when there are other branches ( else if, else etc.) in the statement without curly  braces such as:

if (1)
   INT_SWAP(var_a, var_b);
else
 printf("hello world!\n");

You will find a compilation error:

...
/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

This is because  INT_SWAP(var_a, var_b); the last one  ; has  if terminated the scope, and  else of course the subsequent ones did not find a match  if .

Therefore, there are two solutions, namely not using  ;(port.1) or specifying that (port.2) with curly braces must be used  if, for example:

/* port.1 */
if (1)
   INT_SWAP(var_a, var_b)
else
{
    printf("hello world!\n");
}

/* port.2 */
if (1)
{
   INT_SWAP(var_a, var_b);
}
else
{
    printf("hello world!\n");
}

It can be seen that the non-used  calling method is very awkward in terms of program reading and usage;  it is against common sense ; to stipulate that the calling method with curly braces must be used  , because the macro function should be applicable to any syntax.if

Summary of pros and cons:

  • Advantages: simple and rude.

  • Disadvantage: It cannot be called directly in a statement without curly braces and has a branch  ; it can be  called directly if without  .;

3.  do{...}while(0) way

INT_SWAP After the macro is used and  do{...}while(0) encapsulated, the form is as follows:

#define INT_SWAP(a,b)   \
do{                     \
    int tmp = a;        \
    a = b;              \
    b = tmp;            \
}while(0)

do{...}while(0){} Indicates that the statement within   is executed only once  , and {} the function is consistent with the appearance. The difference is that do{...}while(0) the function macro can be exited early, integrated into a statement and must be used when calling  ;.

Since  do{...}while(0) it is actually a while loop, keywords can be used  break to end the loop early. With this feature, parameter detection can be added to function macros. For example:

#define INT_SWAP(a,b)  \
do{                 \
 if (a < 0 || b < 0) \
  break;   \
    int tmp = a;     \
    a = b;           \
    b = tmp;         \
}while(0)

Since  do{...}while(0); it is actually a syntax, the compiler will  do{...}while(0); treat it as a statement.

Therefore, the function macro of the method can  be directly called in a statement do{...}while(0) without curly braces and with a branch  . ifFor example:

#define INT_SWAP(a,b)  \
do{                 \
 if (a < 0 || b < 0) \
  break;   \
    int tmp = a;     \
    a = b;           \
    b = tmp;         \
}while(0)

int main()
{
 int var_a = 1;
 int var_b = 2;

 if (1)
    INT_SWAP(var_a, var_b);
 else
  printf("hello world!\n"); 
 printf("var_a = %d, var_b = %d\n", var_a, var_b); // var_a = 2, var_b = 1

 return 0;
}

The C language stipulates that do{...}while(0) the syntax must be used  ; as the end of the statement. Therefore it is impossible to have a program with the following statements:

if (1)
   INT_SWAP(var_a, var_b)
else
{
 printf("hello world!\n"); 
}

Summary of pros and cons:

  • if Advantages: Supports direct calls in statements  without curly braces and branches  ; supports early exit from function macros; must be used when calling ;.

  • Disadvantages: no return value, cannot be used as an rvalue of an expression.

4.  ({}) way

({}) Syntax extended to GNU C, not native to C languages.

INT_SWAP After the macro is used and  ({}) encapsulated, the form is as follows:

#define INT_SWAP(a,b)   \
({                      \
    int tmp = a;        \
    a = b;              \
    b = tmp;            \
})

Same  do{...}while(0) as , it supports  direct calls in statements ({}) without curly braces and with branches  . ifFor example:

#define INT_SWAP(a,b)  \
({                 \
 int tmp = a;    \
 a = b;          \
 b = tmp;        \
})

int main()
{
 int var_a = 1;
 int var_b = 2;
 
 if (1)
    INT_SWAP(var_a, var_b);
 else
  printf("hello world!\n");
 printf("var_a = %d, var_b = %d\n", var_a, var_b); // var_a = 2, var_b = 1
 
 return 0;
}

The difference  do{...}while(0) is that ({}) function macros cannot be exited early and return values ​​are supported. ({}) After all, it is not a while loop, and  breakit is easier to understand that the exit function macro cannot be used directly. What does it mean to support return values?

({}) The answer is that the result of the last statement in the C language specification  is the return value of the double-bracket body. For example:

int main()
{
 int a = ({
  10;
  1000;
 });
 printf("a = %d\n", a);      // a = 1000
}

Therefore, ({}) return values ​​can be provided for function macros. For example:

#define INT_SWAP(a,b)  \
({                 \
 int ret = 0;  \
 if (a < 0 || b < 0) \
 {     \
  ret = -1;  \
 }     \
 else    \
 {     \
  int tmp = a;    \
  a = b;          \
  b = tmp;        \
 }     \
 ret;    \
})

int main()
{
 int var_a = 1;
 int var_b = 2;
 
 if (INT_SWAP(var_a, var_b) != -1)
  printf("swap success !!\n");     // swap success !!
 else
  printf("swap fail !!\n"); 
 printf("var_a = %d, var_b = %d\n", var_a, var_b); // var_a = 2, var_b = 1
 
 return 0;
}

It can be seen that  INT_SWAP the macro at this time is very close to the function.

Summary of pros and cons:

  • if Advantages: Supports direct calls in statements without curly braces and branches  ; has a return value, and supports being used as an rvalue of an expression.

  • Disadvantages: does not support early exit function macros; non-C native syntax may not be supported by the compiler.

5. Summary

To sum up,   among the encapsulation methods of these three function macros,  {}, do{...}while(0) and  , should not be used as much as possible . Considering compatibility, it is generally selected to be used  . When the function macro is required to return, it can be considered to use   or directly define the function.({}){}do{...}while(0)({})

Guess you like

Origin blog.csdn.net/weixin_41114301/article/details/130457716