Technology Sharing

【C language】—— Detailed explanation of preprocessing (Part 1)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Predefined symbols

C language sets somePredefined symbols, can be used directly, the predefined symbols are在预处理期间处理的

__FILE__    //进行编译的源文件
__LINE__	//文件当前的行号
__DATE__	//文件被编译日期
__TIME__	//文件被编译那一瞬的时间
__STDC__	//如果编译器遵循ANSI C(标准C),其值为1,否则未定义(报错)
  • 1
  • 2
  • 3
  • 4
  • 5

  
Example:

#include<stdio.h>

int main()
{
	printf("%sn", __FILE__);
	printf("%dn", __LINE__);
	printf("%sn", __DATE__);
	printf("%sn", __TIME__);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
operation result:

insert image description here

Just to mention here, VS does not fully support ANSI C (standard C); g c c gcc gcc Support for ANSI C (Standard C)
  

two,# d e f i n e define define Defining constants (symbols)

Basic syntax:

# define name stuff
  • 1

for example:

#define MAX 100
#define reg register  // 为 register 这个关键字创建一个简短的名字
#define do_forever for(;;)  //用更形象的符号来替换一种实现
#define CASE break;case		//在写 case 语句的时候自动吧 break 写上

//如果定义的 stuff 过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)
#define DEBUG_PRINT printf("file:%stline:%dt 
							date:%sttime:%sn" ,
							__FILE__,__LINE__ , 
							__DATE__,__TIME__ )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • The second sentence is laziness, what's wrong?
  • The third sentence f o r for for Circular初始化、判断、调整都可以省略, but if the judgment is omitted, it means that the judgment condition is always true, that is,Infinite loop
  • It's best not to do this in the fourth game, it's easy to get into trouble
  • Fifth sentenceLine continuation characterIt is to prevent problems after branching. Its essence is转义Back回车符, so that the carriage return is no longer a carriage return. There should be nothing after the line continuation character. Press “” Press Enter directly, otherwise the next line of code will not be continued

  
Now the question is: using # d e f i n e define define When defining an identifier, should we add

for example:

#define MAX 1000
#define MAX 1000;

int main()
{
	int n = MAX;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

insert image description here

The above code plus, it seems a bit redundant, but it has no effect on the program operation
It seems to add Or without It will be all right?
is it really like this?
  
Let’s look at the following example:

//例一
int main()
{
	printf("%dn", MAX);
	return 0;
}

//例二
int mian()
{
	int max = 0;
	if (1)
		max = MAX;
	else
		max = 1;
	return 0
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

After replacement:

printf("%dn", 1000;);
  • 1

Print1000;What means?

if (1)
	max = 1000;
	;
else
	max = 1;
  • 1
  • 2
  • 3
  • 4
  • 5

e l s e else else Who to match?
  
You see, this is the problem, right?use # d e f i n e define define When defining an identifier, do not add

  

three,# d e f i n e define define Defining Macros

  # d e f i n e define define The mechanism includes a provision that允许把参数替换到文本中, this implementation is usually calledMacro ( m a r c o ) (marco) marcoor Defining Macros ( d e f i n e m a c r o ) (define macro) definemacro
The difference between the macro and the macro definition identifier above is:Macros have parameters
  
Here is how the macro is declared:

#define name(parament - list) stuff
  • 1

one of them p a r a m e n t parament parament - l i s t list list(Parameter list) is a comma-separated list of symbols that may appear in s t u f f stuff stuff middle
Note: p a r a m e n t parament parament - l i s t list list(parameter list)Left parenthesisMust be with n a m e name name Close toIf there is any whitespace between them, the argument list is interpreted as s t u f f stuff stuff a part of.
  

Example:

//实现一个宏,计算一个数的平方
#define SQUARE(x) x*x

int main()
{
	int a = 5;
	int ret = SQUARE(a);
	printf("%dn", ret);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
operation result:

insert image description here

As you can see, the square of 5 is calculated correctly.

But in fact the above code is problematic, please see the following code segment:

int a = 5;
printf("%dn", SQUARE(a + 1));
  • 1
  • 2

  
operation result:

insert image description here

Why is this so? 5+1 equals 36, while 6 ∗ * 6 should be 36, how did 11 come about?

The problem is with the macro, we know宏是直接替换The result of directly replacing the above code is:

printf("%dn", a+1*a+1);
  • 1

5 + 6 + 1, the result is 11.

We add brackets on both sides of the macro definition, and this problem is easily solved

#define SQUARE(x) (x)*(x)
  • 1

Is there no problem with this definition? Let's take a look at the following macro definition

#define DOUBLE(X) (X)+(X)
  • 1

We used parentheses in the definition to avoid the previous problem, but this macro may cause new errors.

int a = 5;
printf("%dn", 10 * DOUBLE(a));
  • 1
  • 2

  
operation result:

insert image description here

The output result is not 100 but 55. The reason is similar to the above. The question of priority
  
Solution:

#define DOUBLE(X) ((X)+(X))
  • 1

In summary, when using macrosDon't skimp on bracketsto avoid unexpected interactions between operators or adjacent operators in the arguments when using macros.

  

4. Macro parameters with side effects

When a macro parameter appears more than once in the macro definition, if the parameter hasside effect, then you may be in danger when using this macro, resulting in不可预测A side effect is a permanent effect that occurs when an expression is evaluated.
  
For example:

x + 1; //不带副作用
x++//带副作用
  • 1
  • 2

The following code example of the MAX macro demonstrates the problem caused by parameters with side effects:

#define MAX(a,b) ((a) > (b) ? (a):(b))

int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d, y=%d, z=%dn", x, y, z);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
operation result:

insert image description here

Why is this happening? Let’s analyze it together.

z = ((X++) > (y++) ? (x++) : (y++))
  • 1
  • First, make a judgment: x x x++ and y y y++ judgment, because it is postfix ++, when judging x x x is 5, y y y is 8, 8 &gt; 5
  • After judging x x x is 6, y y y For 9
  • Then execute y y y ++, because it is postfix++, the result is 9
  • Next y y y To auto-increment, y y y The final result is 10

we will x x x and y y y When passed into a macro, the output has changed, especially y y yAfter two changes, do you think it is scary?
When a parameter with side effects is passed to a macro, and the parameter appears more than once in the macro, the side effects of the parameter will also be more than once.
  

5. Macro Replacement Rules

Extending in Programs# d e f i n e define define When defining symbols and macros, several steps are involved

  • When calling the macro, first参数进行检查, see if it contains # d e f i n e define define Defined标识符If so, they are replaced first
  • Replace the text with被插入To the original location in the program, the macro parameter names are replaced by their values
#define MAX(a,b) ((a) > (b) ? (a):(b))
#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, M);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • M A X ( x , M ) MAX(x, M) MAX(x,M) middle M M M First it is replaced by 10, and 10 is inserted into the original M M M Location
  • Finally, the results are scanned again to see if they contain any of the d e f i n e define define Defined symbol, if yes, repeat the above process

In the above code, MAX is also represented by # d e f i n e define define DefinedMacroIn the last test, its parameter M has been replaced. This time it is time to replace it.
   M A X ( x , 10 ) MAX(x, 10) MAX(x,10) Replaced by ( ( x ) > ( 10 ) ((x) > (10) ((x)>(10) ? ? ? ( x ) : ( 10 ) ) (x):(10)) (x):(10))
  
certainly,宏里面嵌套宏it is also fine

MAX(x, MAX(2, 3))
  • 1

At this time, first replace the macro in the parameter, and then replace the entire macro
But it should be noted that this不是递归, which is just a macro as an argument to another macro.递归是宏内部又调用了宏本身
Also, when the preprocessor searches for # d e f i n e define define When defining symbols,The contents of string constants are not searched.
What does it mean? Let me give you an example.

#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, MAX(2, 3));
	printf("M = %d", x);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

In the above codeprintf("M = %d", x);middle M M M It will not be replaced by 10

  

6. Comparison between macros and functions

The above macro to find the larger value of two numbers can be written as a function

int Max(int x, int y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4

We found that they can all perform the same function. However, for the function of "finding the larger value of two numbers", it would be more difficult to write it as a macro.更有优势Some
  
There are two reasons:

  • Used for调用函数and函数返回ofCodeIt may take more time than actually performing the small computation (calling the functionCreate a stack frame). Therefore, macros are more important than functions in a program.规模and速度Better than
  • More importantly函数的参数必须声明类型, which means that the function can only be used on expressions of the appropriate type. On the contrary, this macro can be used for multiple types: integers, long integers, floating point types, etc.
    > to compare.The macro parameters areType-agnosticof

So will we all use macros in the future? Actually, macros are only used inSimple calculationIt is not suitable for complex and large operations and functions compared to macros. Disadvantages

  1. Each time you use a macro, a copy of the macro definition code is inserted into your program. Unless your macro is short, this may大幅度增加程序的长度
  2. Macros are没法调试of
  3. Since macros are type-independent,不够严谨
  4. Macros may cause operation priority problems, causing the program容易出错

But sometimes, macros can do things that functions can't do.
  
For example:

int* p = (int*)malloc(10 * sizeof * (int));
  • 1

We hate writing like this m a l l o c malloc malloc The function is too complicated. I want to大小and类型Pass it over to open up space

Malloc(10, int);
  • 1

Can a function do this? No, becauseFunctions cannot pass types
Macros can do this, because macros are notNo inspectionYour parameterstypeof

#define Malloc(n, type) (type*)malloc(n * sizeof(type))

int main()
{
	int* p = Malloc(10, int);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  
A comparison between macros and functions:

Attributes# d e f i n e define defineDefining Macrosfunction
Code lengthEach time a macro is used, it is inserted into the program, and except for very small macros, the program length can grow substantially.The function code value appears in one place, and each time the function is used, the same code in that place is called
Execution speedFasterThere is additional overhead for calling and returning functions (opening stack frames), so
Operator precedenceMacro parameters are evaluated in the context of all surrounding expressions. Unless parentheses are added, the precedence of adjacent operators may have unpredictable consequences. Therefore, it is recommended to use more parentheses when writing macros.Function parameters are evaluated only once when the function is called, and their result value is passed to the function, making the evaluation result of the expression easier to predict.
Parameters with side effectsParameters may be substituted into multiple locations within a macro. If a macro's parameters are evaluated multiple times, parameter evaluation with side effects may produce unpredictable results.Function parameters are only called once when passing them, and the results are more predictable
Parameter TypeThe parameters of the macro are independent of type. As long as the operation on the parameters is legal, it can be used for any parameter type.Function parameters are type-dependent. If the types of the parameters are different, different functions are required, even if they perform the same task.
debugMacros are not easy to debugFunctions can be debugged line by line
recursionMacros cannot be recursiveFunctions can be recursive

Is there any way to combine their advantages?
Introduced in C++Inline functions i n l i n e inline inline —— It has the advantages of both macros and functions
It executes as fast as a macro, but has the same effect as a function.