Original: Advanced application of C language macros
On the # and # #在C语言的宏中, #的功能是将其后面的宏参数进行字符串化操作 (stringfication), simply put a double quote in the left and right side of the macro variable that it refers to by replacing it. For example, a macro in the following code:
#define WARN_IF (exp) do { IF (exp) "" " while (0)
Then the replacement procedure shown in the actual use will appear as follows:
0 ), replaced by do {if0) "Warning "divider = = 0""" while (0);
This way, each time the divider (divisor) is 0, a message is printed on the standard error stream.
and # #被称为连接符 (concatenator), used to connect two tokens to a token. Note that the object connected here is token, not necessarily a macro variable. For example, you want to make a menu item command name and function pointer composition of the structure of the array, and you want to have the function name and menu item command name has an intuitive, name relationship. Then the following code is very useful:
struct command { char * name; void (*function) (void);}; #define Name, name # # _command}
Then you can easily initialize an array of command structures with some pre-defined commands:
struct command commands[] = {command (quit), command (Help), ...}
The command macro acts as a code generator here, which reduces the code density to a certain extent, and indirectly reduces the errors caused by careless attention. We can also N # #符号连接 n+1 A token, this feature is not available in the # symbol. Like what:
#define Link_multiple (a,b,c,d) a# #_ # #b # #_ # #c # #_ # #dstruct _record_type link_multiple
(name,company,position,salary);
// here This statement will be expanded to: // typedef struct _RECORD_TYPE name_company_position_salary
About... The use
... In the C macro, it is called the Variadic macro, which is the variable parameter. Like what:
#define MYPRINTF (Templt,...) fprintf (stderr,templt,__va_args__)
Or
#define MYPRINTF (Templt,args ...) fprintf (Stderr,templt,args)
In the first macro, we replace it with the default macro __va_args__ because it is not named for the variable. In the second macro, we explicitly name the parameter args, then we can use args to refer to the arguments in the macro definition. As with the C language stdcall, the argument must appear as the most important parameter in the table. When we can only provide the first parameter Templt in the above macro, the C standard requires that we must write:
myprintf (TEMPLT,);
The form. The replacement process at this point is:
myprintf ("error!",);
To be replaced by:
fprintf (stderr, "error!",);
This is a syntax error and cannot be compiled properly. There are generally two workarounds for this problem. First, the workaround provided by GNU CPP allows the above macro invocation to be written as:
myprintf (TEMPLT);
And it will be replaced by:
fprintf (stderr, "error!",);
Obviously, there is still a compilation error (not a compilation error in some cases, not in this case). In addition to this approach, C99 and GNU CPP support the following macro definition methods:
#define MYPRINTF (TEMPLT, ...) fprintf (STDERR,TEMPLT, # #__VAR_ARGS__)
At this point, # #这个连接符号充当的作用就是当__VAR_ARGS__为空的时候, remove the comma from the front. So the translation process at this point is as follows:
myprintf (TEMPLT);
be converted to:
fprintf (STDERR,TEMPLT);
This will not generate a compilation error if the TEMPLT is valid.
Incorrect nesting-misnesting
The definition of a macro does not necessarily have to have a complete, paired parenthesis, but it is best to avoid such use in order to avoid errors and improve readability.
Problems caused by operator precedence-operator precedence problem
Since macros are simply replacements, if the parameters of a macro are compound, then it may be unexpected if we do not use parentheses to protect individual macro parameters, because the operator precedence between the arguments is higher than the operator precedence of the individual parameters. Like what:
#define CEIL_DIV (x, y) (x + y-1)/y
So
A = Ceil_div (b & C, sizeof (int));
will be converted to:
A = (b & C
Since the priority of +/-is higher than the priority of &, the above equation is equivalent to:
A = (b & (c + sizeof (int)-1))/sizeof (int);
This is clearly not the intention of the caller. To prevent this from happening, you should write more than a couple of parentheses:
#define CEIL_DIV (x, Y) (((x) + (y)-1)/(y))
Eliminate redundant semicolons-semicolon swallowing
Typically, to make a macro look like a normal C-language call on the surface, we usually add a semicolon to the macro, such as the following parameter macro:
My_macro (x);
But if the following is the case:
/* Line 2 */
}
//...
if (condition ()) My_macro (a); else {...}
This will result in a compilation error due to the extra semicolon. To prevent this from occurring while maintaining My_macro (x), we need to define the macro as this form:
#define MY_MACRO (x) do {/* Line 1 */* Line 2 */* Line 3 */} while (0)
This way, there is no problem as long as the semicolon is always used.
Duplication of Side Effects
The side effect here means that the macro can be evaluation (that is, the value) of its arguments when it is expanded, but if the macro argument is a function, it is possible to be called multiple times to achieve inconsistent results, even more serious errors. Like what:
#define MIN (x, y) > (Y)? (Y): (X))
//...
c = min (A,foo (b));
The Foo () function is then called two times. To solve this potential problem, we should write this macro as min (x, y):
#define MIN (x, y) ({typeof x_ = (x); typeof (Y) Y_ = (y); (X_ < Y_)? X_: Y_; })
({...}) is to return the value of the last bar in several statements inside, and it also allows the variable to be declared internally (because it makes up a partial scope through curly braces)
Advanced apps for C-language macros