Three Encapsulation Methods for Function Macros in C Language

1 Introduction to Function Macros

Function macros are macro definitions that contain multiple statements, typically encapsulating frequently called functionalities without the overhead of function calls, such as stack push and pop.

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

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

However, the above macro has a significant drawback: when used with control statements like if or while without braces, the effective scope ends after the first semicolon of the macro. Thus, both a = b and b = tmp are not controlled by the conditional statement.Therefore, in practice, three methods are generally used to encapsulate function macros: {} , do{…}while(0) , and ({}) . The following sections will analyze and compare the advantages and disadvantages of each method.2 Method using { }The INT_SWAP macro encapsulated with {} looks like this:

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

At this point, it can be called directly and will work correctly in control statements without braces (like if or while), for example:

#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}

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

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

It will result in 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 the final ; of INT_SWAP(var_a, var_b); has already terminated the scope of the if, and the subsequent else cannot find a matching if.Thus, there are two solutions: either do not use ; (port.1) or require that if statements must use braces (port.2), 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 calling method without using ; is quite awkward in terms of program readability and usage; while requiring if statements to use braces is counterintuitive, as macro functions should be applicable to any syntax.Summary of advantages and disadvantages:

Advantages: Simple and straightforward.Disadvantages: Cannot be directly called in if statements without braces that have branches; can be called without ;.

3 Method using do{…}while(0)The INT_SWAP macro encapsulated with do{…}while(0) looks like this:

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

do{…}while(0) indicates that the statements within {} are executed only once, which is functionally similar to {}. The difference is that do{…}while(0) allows for early exit from the macro, can be combined into a single statement, and requires a ; when called.Since do{…}while(0) is essentially a while loop, the break keyword can be used to exit the loop early. This feature allows for parameter checking in 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); is treated as a single statement by the compiler, the do{…}while(0) method of function macros can be called directly in if statements without braces that have branches. 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 specifies that the do{…}while(0) syntax must use ; as the statement terminator. Therefore, the following statement cannot occur:

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

Summary of advantages and disadvantages:

Advantages: Supports direct calls in if statements without braces that have branches; allows early exit from the macro; requires a ; when called.Disadvantages: No return value, cannot be used as an expression’s right value.

4 Method using ({ })({}) is a syntax extension of GNU C, not part of the native C language.The INT_SWAP macro encapsulated with ({}) looks like this:

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

Similar to do{…}while(0), ({}) supports direct calls in if statements without braces that have branches. 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;}

Unlike do{…}while(0), ({}) does not allow early exit from the macro and supports return values. The meaning of supporting return values is that the last statement within the ({}) is considered the return value of that double-bracketed body.For example:

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

Thus, ({}) can provide return values 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 the INT_SWAP macro is now very close to a function.Summary of advantages and disadvantages:

Advantages: Supports direct calls in if statements without braces that have branches; has return values, can be used as an expression’s right value.Disadvantages: Does not support early exit from the macro; non-native C syntax, compiler may not support it.

5 ConclusionIn summary, among the three encapsulation methods for function macros: {} , do{…}while(0) , and ({}) , it is advisable to avoid using {} , generally choose do{…}while(0} for compatibility, and consider using ({}) or directly defining functions when return values are needed.Original article:https://blog.csdn.net/qq_35692077/article/details/102994959

Leave a Comment