I. Concept
A macro only acts on the text of a program. It provides a way to transform the characters of a program, instead of the objects in the program, therefore, a piece of code that looks completely illegal can be converted into a valid program, and a seemingly harmless code can be programmed as a monster.
Ii. Details
1) spaces in macro definition (note the macros with parameters)
If the function has no parameters, you only need to add a pair of parentheses after the function name. If a macro does not contain parameters, you only need to use the macro name. Parentheses are irrelevant.
# Define f (x)-1:
Use F to represent (x)-1) instead of f (x) To represent (x)-1), because f is followed by a space!
Macro definitions of time spaces affect their meanings, but calls of time spaces are irrelevant.
For example, when # Define fun (x)-1) is called, The results of fun (3) and fun (3) are both 2.
2) macros are not functions (auto-increment and auto-subtraction should be taken as parameters with caution)
In macro definition, it is best to enclose each parameter in parentheses, and the entire expression is also enclosed in parentheses.
However, even if the macro definition parameters and the entire expression are included, other problems may still exist.
Example: # define max (A, B) (a)> (B )? (A): (B ))
When expression A is greater than expression B, if expression A is an auto-incrementing expression, expression A is evaluated repeatedly, resulting in an incorrect final result.
We can implement the toupper function as follows:
Char toupper (int c)
{
If (C> = 'A' & C <= 'Z ')
C-= 'a'-'A ';
Return C;
}
Macro-defined implementation is much faster than calling a function, but it is dangerous:
# Define toupper (C) (c)> = 'A' & C <= 'z '? (C)-('A'-'A'): (c ))
The result is incorrect when toupper (* P ++) is called like this!
3) The macro is not a statement (note that the macro contains C statements)
Assert (x> Y); Assert is a macro. When the parameter is 0, the file name of the asserted failure and the row number of the failed part are reported. When the parameter is not 0, nothing is done.
If the definition is as follows: # define assert (e) if (! E) assert_error (_ file __,__ line __)
An error occurred when calling the following:
If (x> 0 & Y> 0)
Assert (x> Y );
Else
Assert (Y> X );
The process structure of if else is incorrect.
Modify:
Parentheses are added for definition: # define assert (E) (if (! E) assert_error (_ file __,__ line __))
However, in the above call, the end of assert contains a semicolon, resulting in a syntax error.
The correct definition is as follows:
# Define assert1 (E) (void) (e) | _ assert_error (_ file __,__ line __)))
This definition is similar to an expression rather than a statement.
4) macros are not type definitions.
A common purpose of macros is to make the types of multiple different variables be described in one place:
# Define footype struct foo
The types of footype A; footype B, C; and A, B, and C can be changed.
However, typedef is more common!
# Define T1 struct Foo * when trying to declare multiple variables, the problem arises:
T1 a, B; is extended to struct Foo * a, B; at this time, a is the pointer, and B is the struct.
The following definition is used: typedef struct Foo * t2;
T2 c, d at this time, C and D are pointer to the struct, T2 behavior is exactly the same as a new type of behavior.
Iii. Exercise
1) Use a macro to implement max. The Max parameters are integers, and these integer parameters must be evaluated only once.
Answer: because each parameter value is used twice, one is returned when the parameter is compared and the other is returned when the parameter value is used as the result. Therefore, each parameter value should be stored in a temporary variable.
However, we cannot declare a temporary variable within a c expression, and even if we can declare a temporary variable to call Max multiple times, it will cause repeated definitions. Therefore, these variables cannot be declared as part of the macro definition, but should be excluded from the macro definition. When Max is used in more than one program file, these variables should be declared as static to avoid naming conflicts.
Static
Int tmp1, tmp2;
# Define max (p, q) (tmp1 = (P), tmp2 = (Q), tmp1> tmp2? Tmp1: tmp2)
In this case, max cannot be nested, or it cannot work normally.
2) # is define f (x)-1) A legal expression?
Answer:
(1) it can be used when X is a type name. For example: # define x int, then (INT)-1) indicates that-1 is forcibly converted to the int type twice.
(2) when X is a function pointer and X points to an element of a function pointer array, you can. The expression can be interpreted as calling the function pointed to by X, and (x)-1) is the parameter of the function. Assume that the type of X is T, that is, t x. t is defined as follows:
Typedef void (* t) (void *), because void * can be forcibly converted to T type. But it cannot be defined as follows:
Typedef void (* t) (T), Because t can be defined only after T is declared!