The use of macros is a very high skill, good use can save a lot of code, and bad use will appear unexpected bugs, this article

A brief look at the use of macros

Before compiling, the preprocessor analyzes the source file, and when it encounters a macro name, the preprocessor expands the macro, replacing the macro name with defined text.

The definition of a macro

A macro with no arguments

#defineMacro name replaces text
Copy the code

Macros with arguments

When the preprocessor expands such a macro, it first replaces the corresponding parameter in the replacement text with the actual parameter (referred to as the “argument”) specified when the macro is called. Macros with corporeal parameters are also commonly called function-like macros

#defineMacro name ([parameter list]) replaces text
#defineMacro name ([parameter list,]...) Replace text
Copy the code

C99 allows macros to be called with their argument list empty. In this case, the parameter in the corresponding replacement text is not replaced; That is, replacing the text removes the parameter. However, not all compilers support this “empty argument” approach.

If you call an argument that also contains a macro, it would normally be expanded before replacing the argument with a parameter in the replacement text. This is handled differently in cases where the parameters in the replacement text are # or ## operands.

Optional parameter in macro__VA_ARGS__

The C99 standard allows macros to be defined with ellipses, which must be placed after the argument list to indicate optional arguments. You can call such macros with optional arguments. When a macro with optional arguments is called, the preprocessor wraps all the optional arguments together with a comma separating them as one argument. In the replacement text, the identifier __VA_ARGS__ corresponds to a set of the previously packaged optional arguments. The __VA_ARGS__ identifier can only be used in substitution text at macro definition time. __VA_ARGS__ behaves like any other macro argument, except that it is replaced by all remaining arguments in the argument list used when the call is made, rather than just one. Here is an example of an optional parameter macro:

// Suppose we have an open log file and want to write to it using the file pointer fp_log
#define printLog(...) fprintf( fp_log, __VA_ARGS__ )
// Use the macro printLog
printLog( "%s: intVar = %d\n", __func__, intVar ); The preprocessor replaces the last line of macro calls with the following line:fprintf( fp_log, "%s: intVar = %d\n", __func__, intVar );
Copy the code

The predefined identifier __func__, which is a string representing the name of the current function, can be used in any function.

Operators in macros

String-like operator#

It converts the macro call argument to a string. The operand of # must be a parameter in the macro replacement text. When a parameter name appears in the replacement text and is prefixed with the # character, the preprocessor places the argument corresponding to the parameter in a pair of double quotes, forming a string literal.

All characters in arguments themselves remain the same, with the following exceptions:

  1. If there is a sequence of whitespace characters between tokens of arguments, it is replaced with a space character.
  2. Each double quote (“) is preceded by a backslash (\) in the argument.
  3. A backslash is also added to precede each backslash in character constants and string literals. However, if the backslash itself is part of the common character name, it is not preceded by a backslash.
#define printDBL( exp ) printf( #exp " = %f ", exp )
printDBL( 4 * atan(1.0));           // atan() is defined in math.hThe last line above is a macro call, expanded as follows:printf( "4 * atan (1.0)" " = %f ".4 * atan(1.0)); Because the compiler merges adjacent string literals, the above code is equivalent to:printf( "4 * atan(1.0) = %f".4 * atan(1.0));

#define showArgs(...) puts(#__VA_ARGS__)
showArgs( one\n,       "2\n", three ); The preprocessor replaces the macro with the following text:puts("one\n, \"2\\n\", three");
Copy the code

The token paste operator# #

The operator is a binary operator that can appear in the replacement text of all macros. The operator combines the left and right operands as one token. If the resulting text also contains the macro name, the preprocessor proceeds with the macro replacement. Whitespace before and after the ## operator is removed, along with the ## operator itself.

#define TEXT_A "Hello, world!"
#define msg(x) puts( TEXT_ ## x )msg(A); Whether the identifier A is defined as A macro name or not, the preprocessor replaces the parameter X with the argument A and then pastes the token. When these two steps are done, the result is as follows:puts( TEXT_A ); Because TEXT_A is a macro name, subsequent macro replacements produce the following statement:puts( "Hello, world!" );
Copy the code

Macros are used within macros

Note that macros with ‘#’ or ‘##’ are not expanded

#define A          (2) 
#define STR(s)     #s 
#define CONS(a,b)  int(a##e##b) 

printf("int max: %s\n",  STR(INT_MAX));    / / INT_MAX # include < climits >The trade will be expanded as:printf("int max: %s\n"."INT_MAX"); 

printf("%s\n", CONS(A, A));               // compile error  This line is:printf("%s\n".int(AeA)); 
Copy the code

The solution to this problem, however, is simple. Add one more layer of intermediate conversion macros. The purpose of adding this macro layer is to expand all the macro parameters in this layer, so that the macro (_STR) in the transformation macro will get the correct macro parameters.

#define A           (2) 
#define _STR(s)     #s 
#define STR(s)      _STR(s)          / / conversion macros
#define _CONS(a,b)  int(a##e##b) 
#define CONS(a,b)   _CONS(a,b)       / / conversion macros

printf("int max: %s\n", STR(INT_MAX));          // INT_MAX, the maximum value of type int, is a variable # include
      The output is:int max: 0x7fffffff 
STR(INT_MAX) -->  _STR(0x7fffffff) and convert it to a string;printf("%d\n", CONS(A, A)); The output is:200 
CONS(A, A)  -->  _CONS((2), (2) -- -- >int((2)e(2)) 
Copy the code

Macro scoping and redefinition

You cannot redefine an identifier that has been defined as a macro again using the #define command unless the replacement text used for the redefinition is exactly the same as the replacement text already defined. If you want to change the contents of a macro, you must first undefine it using the following command:

#undefThe name of the macro
Copy the code

The first time a macro encounters its #undef command, its scope ends. If there is no #undef command on the macro, its scope terminates at the end of the translation unit.

reference

C macros Definition and usage of macros The usage of “#” and “##” in C macros Advanced usage of C macros