"C Pitfalls and Defects"----Chapter 6 The Preprocessor

Key point: Macros only act on the text of the program.

6.1 Spaces in macro definitions cannot be ignored

Observe the following macro definition:

#define f (x) ((x)-1)

f(x) stands for

(x) ((x)-1)

and we want it to represent

((x)-1)

The reason is that there is an extra space between the f and the following (x)! So, the correct way to define it is:

#define f(x) ((x)-1)

This rule does not apply to macro calls, but only to macro definitions. So, after completing the macro definition above, both f(3) and f(3) evaluate to 2. Note: The function call is exactly the same, that is, if we define the function ADD, then the result of evaluating ADD(1,2) and ADD(1,2) is 3.

6.2 Macros are not functions

Note: Each parameter and the entire result expression in the macro definition are enclosed in parentheses. But there are still other problems, such as the self-increment and self-decrement operators of side effects. As long as they appear once, they will perform an auto-increment or a self-decrement. In the final analysis, because the macro is a text replacement, **To ensure that the parameters of the macro have no side effects. ** This is not the case with functions. The increment or decrement operator in the function argument will only be executed once, so it is necessary to distinguish between functions and macros.

6.3 Macros are not statements

Suppose we define a macro that can include the filename and the line number where the assertion failed in the error message, i.e.

assert(x<y);

Do nothing when x is greater than y, otherwise terminate the program.

If we define it like this:

#define assert(e) if(!e) assert_error(__FILE,__LINE__)

But an error occurs when we encounter the following situation:

if(x > 0 && y > 0)
	assert(x > y);
else
	assert(y > x);

The above notation looks like this when expanded:

if(x > 0 && y > 0)
	if(!(x>y)) assert_error("foo.c",37);
else
	if(!(y>x)) assert_error("foo.c",39);

After indenting the above code, it looks like this:

if (x > 0 && y > 0)
		if (!(x > y)) assert_error("foo.c", 37);
		else
			if (!(y > x)) assert_error("foo.c", 39);

Obviously, the above code is completely different from what we want to express.

Of course, it seems that we can use braces to enclose the macro in the definition of the macro assert, which can avoid such problems:

#define assert(e)\
	{if(!e) assert_error(__FILE,__LINE__);}

However. This will bring new problems. The example we mentioned above becomes:

if(x > 0 && y > 0)
	{ if(!(x<y)) assert_error("foo.c",37);};
else
	{ if(!(y > x)) assert_error("foo.c",39);};

A semicolon before the else is a syntax error. One solution to this problem is to add a semicolon to the assert call, but this usage is a bit "weird":

y = distence(p,q);
assert(y>0)
x = sqrt(y);

The correct definition of the macro assert is quite counterintuitive, this definition looks like an expression, not like a statement:

#define assert(e)\
	((void)((e)||_assert_error(__FILE__,__LINE__)))

This definition actually takes advantage of the property that the || operator evaluates both operands sequentially.

6.4 Macros are not type definitions

Macros are just simple text replacements, but typedefs define new types, which have the same status as int, float, etc.

practise

  1. Q: Please use a macro to implement a version of max where the arguments to max are all integers, requiring these integer arguments to be evaluated only once in the definition of the macro max.

    answer:

    static int max_temp1,max_temp2;
    #define max(p,q) (max_temp1 = (p),max_temp2 = (q),\
    	max_temp1>max_temp2? max_temp1:max_temp2)
    

    The above definition works fine as long as the max macro is not nested: in the case of nested calls to the max macro, it doesn't work.

  2. Question: Can this expression (x) ((x) - 1)be called a valid expression?

    Answer: It is possible, and there are two situations:

    The first case:

    typedef int x;
    (x) ((x)-1);
    //等价于下面的表达式
    (int) ((int)-1)
    

    Second case:

    x is a function pointer, x points to an element of an array of function pointers

    typedef void(*T)(void*);
    T x;
    

Guess you like

Origin blog.csdn.net/m0_57304511/article/details/123758784