Detailed preprocessing (two)-#define defines macros and identifiers + comparison of macros and functions + the role of # and ##

#definedefine identifier

The format of #definedefine identifiers is as follows:

#define MAX 100
#define reg register//懒人觉得register太长了

These identifiers defined by #define will be replaced by the compiler with the corresponding content in the preprocessing stage. I saw a more interesting thing before, and I will share it with you here:

#define mian main
#define , ,
#define ( (
#define ) )
#define ture true
#define ; ;

Indeed, as long as you add these words in front of the file, you no longer have to worry about writing the punctuation marks in your code in Chinese, and you no longer have to worry about writing main as mian and true as ture, because even if you make a mistake It has also been replaced with the correct one. But we should be careful when writing code to avoid these low-level errors.

#definedefine macros

The #define mechanism includes a provision that allows parameters to be substituted into the text. This implementation is usually called a macro or define macro.

For example, use a macro to achieve the square of a number:

#include <stdio.h>
#define SQUARE(x) x*x//求x的平方
int main()
{
    
    
	int ret = SQUARE(5);
	//相当于int ret = 5*5;
	printf("%d\n", ret);//结果为25
	return 0;
}

But this is not completely correct, because when the macro parameter you pass in is 2+3, the printed result is not 25, but 11. Because the macro completes the replacement, it will not first calculate the value of 2+3 and then replace it, but replace it directly, so passing in 2+3 is equivalent to:

	int ret = 2+3*2+3;

Because * has a higher priority than +, the result of this calculation is of course 11. In order to avoid this situation, use a macro to achieve the square of a number should be like this:

#define SQUARE(x) ((x)*(x))

The reason for enclosing (x)*(x) in parentheses is the same, to avoid unpredictable consequences caused by the priority of operators when using macros.

So when using #define to define a macro, don't be stingy with parentheses, and add them wherever they should be .

#define substitution rules

When replacing macros and identifiers defined by #define in a program, several steps are involved :
Let's use the following code as an example:

#include <stdio.h>
#define MAX 100
#define SQUARE(x) ((x)*(x)*MAX)
int main()
{
    
    
	int ret = SQUARE(5);
	printf("%d\n", ret);
	return 0;
}

1. When calling the macro, first check the parameters to see if it contains any symbols defined by #define. If they are, they are replaced first.
For example, if the macro defined by #define contains the symbol MAX defined by #define, when calling this macro, first replace MAX.

#include <stdio.h>
#define SQUARE(x) ((x)*(x)*100)
int main()
{
    
    
	int ret = SQUARE(5);
	printf("%d\n", ret);
	return 0;
}

2. The replacement text is then inserted into the original text position in the program. For macros, the parameter names are replaced by their values.
For example, after this step in the above example, the code is equivalent to:

#include <stdio.h>
int main()
{
    
    
	int ret = ((5)*(5)*100);
	printf("%d\n", ret);
	return 0;
}

3. Finally, scan the result file again to see if it contains any symbols defined by #define. If it is, repeat the above process.
The above example no longer contains any symbols defined by #define.

Note:
1. Variables defined by other #define can appear in macro parameters and #define definitions. But for macros, recursion cannot occur.
That is, code similar to the following cannot appear:

#define FAC(x) (x)*FAC(x-1)//error

2. When the preprocessor searches for symbols defined by #define, the contents of string constants are not searched.
For example, MAX in the following code string will not be replaced with 100, but MAX outside the string will be replaced.

#include <stdio.h>
#define MAX 100
int main()
{
    
    
	printf("MAX = %d\n", MAX);//结果为MAX = 100
	return 0;
}

Macro parameters with side effects

Before introducing macro parameters with side effects , let's take a look at what it means to have side effects .

	int a = 10;
	int b = a + 1;//无副作用
	int c = a++;//有副作用

In the code, both b and c want to get the value of a+1, but do not change the value of a. After b gets the value of a+1, the value of a does not change, so there is no side effect; but after c gets the value of a+1, the value of a also changes, that is, there are side effects. Simply put, after the code is executed, in addition to achieving the desired result, it also causes other problems. We say that the statement has side effects.

When a macro parameter appears more than once in the definition of a macro, if the parameter has side effects, then you may be dangerous when using this macro, leading to unpredictable consequences. Side effects are permanent effects that occur when an expression is evaluated.
For example, we want to compare the size of a and b, and assign the larger value to c, and then add 1 to both a and b.

#include <stdio.h>
#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
    
    
	int a = 10;
	int b = 20;
	int c = MAX(a++, b++);
	printf("%d\n", c);
	return 0;
}

This code seems to have no problem, but the result is incorrect, because after the macro is replaced, it is equivalent to the following code:

#include <stdio.h>
int main()
{
    
    
	int a = 10;
	int b = 20;
	int c = ((a++)>(b++)?(a++):(b++));
	printf("%d\n", c);
	return 0;
}

After the replacement, we can get the answer as soon as we analyze it. The final result of c is 21, and after the code is executed, the values ​​of a and b are not increased by 1 at the same time, the value of a has changed to 11, and the value of b has changed. For 22.

Therefore, when we use macros, we should avoid passing in macro parameters with side effects .

Comparison of macros and functions

Macros are usually used to perform simple operations . For example, find the larger of two numbers.

#define MAX(x,y) ((x)>(y)?(x):(y))

Then why not use the following function to achieve this function?

int Max(int x, int y)
{
    
    
	return x > y ? x : y;
}

1. The code used to call the function and return from the function may take more time than the actual execution of this small calculation work. So macros are superior to functions in terms of program size and speed.

2. More importantly, the parameters of the function must be declared as a specific type. So functions can only be used on expressions of the appropriate type. But the macro can be applied to the types that can be compared with integers, long integers, floating-point types, etc. Macros are type independent.

Moreover, macros can sometimes do things that functions cannot. For example, macro parameters can have types, but functions cannot .
When we use the malloc function to open up memory space, we may feel that there is too much code.

#include <stdio.h>
#include <stdlib.h>
int main()
{
    
    
	int* p1 = (int*)malloc(10 * sizeof(int));
	if (p1 == NULL)
	{
    
    
		printf("p1开辟失败\n");
		return 1;
	}
	free(p1);
	p1 = NULL;
	return 0;
}

At this time, we can implement a macro so that when we open up space with malloc, we only need to pass in the opened type and the number of elements of that type.

#include <stdio.h>
#include <stdlib.h>
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
int main()
{
    
    
	int* p2 = MALLOC(10, int);
	if (p2 == NULL)
	{
    
    
		printf("p2开辟失败\n");
		return 1;
	}
	free(p2);
	p2 = NULL;
	return 0;
}

However, macros also have disadvantages, such as:

  1. Every time a macro is used, a copy of the macro definition code will be inserted into the program. Unless the macro is relatively short, it may greatly increase the length of the program.
  2. Macros cannot be debugged.
  3. Since macros are not type-independent, they are not rigorous enough.
  4. Macros may cause problems with operator precedence, making the process prone to errors.

Below we have drawn a framework diagram, which can more clearly distinguish the difference between macros and functions:
Insert picture description here

Unpopular knowledge points: # and ##

The use of # and ## mentioned here is very rare, and some bloggers may not have heard of it, but this knowledge point may also be tested during the interview, which is also more important.
1. The function
of # The # mentioned here is not the # in #define and #include. The function of # mentioned here is to turn a macro parameter into a corresponding string .

So, what is the actual function of this #?
Before introducing the function of #, let me explain to you: Strings have the characteristics of automatic connection .
For example, the following case:

	char arr[] = "hello ""world!";
	//等价于char arr[] = "hello world!";
	printf("helll ""world!\n");
	//等价于printf("helll world!\n");

Next, I will give you a use case of #. For example, there is the following code:

#include <stdio.h>
int main()
{
    
    
	int age = 10;
	printf("The value of age is %d\n", age);
	double pi = 3.14;
	printf("The value of pi is %f\n", pi);
	int* p = &age;
	printf("The value of p is %p\n", p);
	return 0;
}

We found that most of the content to be printed by printf is the same. So, in order to avoid code redundancy, can we encapsulate it into a function or macro?
After thinking and experimenting, it is found that neither functions nor ordinary macros can achieve this function. Bloggers who don’t believe it can go to test.
At this time, you need to use this #, the code is as follows:

#include <stdio.h>
#define print(data,format) printf("The value of "#data" is "format"\n",data)
int main()
{
    
    
	int age = 10;
	print(age, "%d");
	double pi = 3.14;
	print(pi, "%f");
	int* p = &age;
	print(p, "%p");
	return 0;
}

At this time, we only need to pass in the variable name and print format of the variable to be printed. This code is equivalent to the following code after preprocessing:

#include <stdio.h>
int main()
{
    
    
	int age = 10;
	printf("The value of ""age"" is ""%d""\n", age);
	double pi = 3.14;
	printf("The value of ""pi"" is ""%f""\n", pi);
	int* p = &age;
	printf("The value of ""p"" is ""%p""\n", p);
	return 0;
}

And because the character string is automatically connected, the desired result can be printed out.
2.##的
效应## can combine the symbols on both sides of it into one symbol. It allows macro definitions to create identifiers from separate text fragments .

For example, the macro defined below can combine two incoming symbols into one symbol.

#include <stdio.h>
#define CAT(x,y) x##y
int main()
{
    
    
	int workhard = 100;
	printf("%d\n", CAT(work, hard));//打印100
	return 0;
}

Guess you like

Origin blog.csdn.net/chenlong_cxy/article/details/115267432