Table of contents
1. Introduction to function macros
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 . if
For 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 . if
For 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 break
it 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)
({})