Three Encapsulation Methods for Function Macros in C Language

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 stack operations associated with function calls.

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 in the macro. Thus, both a = b and b = tmp are not controlled by the surrounding control 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 using {} is as follows:

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

At this point, it can be called directly and will function correctly within control statements (like if or while) without braces, 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 an 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 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 using do{…}while(0) is as follows:

#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 integrated into a single statement, and requires a ; when called.Since do{…}while(0) is effectively 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 a right-hand value in expressions.4 Method using ({ })({}) is a GNU C extension syntax, not part of the native C language syntax.The INT_SWAP macro encapsulated using ({}) is as follows:

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

However, unlike do{…}while(0), ({}) does not allow early exit from the macro and supports return values. The ability to return values means that the last statement within the ({}) is considered the return value of that block.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 right-hand values in expressions.Disadvantages: Does not support early exit from the macro; non-native C syntax, may not be supported by all compilers.5 ConclusionIn summary, among the three encapsulation methods for function macros: {} , do{…}while(0) , and ({}) , it is advisable to avoid using {} , consider using do{…}while(0) for compatibility, and use ({}) or define a function when return values are needed.

Three Encapsulation Methods for Function Macros in C Language

Three Encapsulation Methods for Function Macros in C Language

Some screenshots of electronic books

Three Encapsulation Methods for Function Macros in C Language

【Complete Set of Hardware Learning Materials Collection】

Three Encapsulation Methods for Function Macros in C Language

Leave a Comment