[In-depth analysis of C language] preprocessing (full)

foreword

First come to two pictures to review the basic content of preprocessing
insert image description here
insert image description here

Combining these two figures, we can get

1. Each source file needs to be compiled by a separate compiler to generate an object file (obj)
2. The linker links the object file and the link library to obtain an executable program (suffix . exe)
3. The executable program is actually a text file that can run.

And the new questions we need to raise are

1. What exactly is included in the header file? What are the two ways of linking?
2. Which one comes first for the replacement of comments and the replacement of macros? How to prove?

Supplement: Program environment and preprocessing - if you want to review the previous preprocessing, you can read this article.

1. Pretreatment

head File

We must first open the header file to see what is in it?
First note: I am in the version of VS2019, and what I opened is our most commonly used stdio.h header file

insert image description here
insert image description here

First of all, let me explain: I can't understand what is contained in these header files (don't spray the chicken), we can only roughly look at the structure inside.

Conclusion: The header file roughly includes header files, function declarations, macro definitions, preprocessing instructions, type redefinition, etc.

We usually think that the library functions are all in the header file, then we have to ask again, if what we include are only function declarations, where are the library functions we use? The answer is in the link. Then we may have to ask again, how to link?

The linker links the object file and the link library, and other links do not include external files, so the answer comes naturally, and the definition of the function is in the link library. In addition, the links we know are actually divided into two types according to the use of the link library:

Static link: Copy and paste the content of the static library (which we can understand as the definition of the library function) into the executable file. The deletion of the static library here will not affect the operation of the program. This guarantees the complete independence of the executable program.

Dynamic link: When linking, find the content to be linked, and then generate a link with the executable file, find and use the linked content in a certain way, so delete the dynamic library, and the executable program cannot run. In this way, multiple copies of code can use the same library to save a certain amount of space consumption.
Summary: Static link: copy and paste, complete and independent (features). Dynamic link: indirect use, one code for multiple purposes.

Macros and Comments

Let's look at this code first:

#include<stdio.h>
#define A //
int main()
{
    
    
	A printf("hello world\n");
	return 0;
}

Hypothetical argument:
If the macro is replaced first, the content in the printf function will not be executed; otherwise, if the comment is deleted first, the content of the printf function will be executed.

Result:
The comment is deleted first, and the printf content will be executed.

picture:
insert image description here

Shorthand tips: preprocessing, precompilation, compiled Linux instructions: -E, -S, -C.

2. Explain the macro in detail

note

#define A /*
#define B */

Parsing:
/* first matches */, so /*#define B */ is the comment part.

Can macros be used in strings?

#define MAX 100
int main()
{
    
    

	printf("%d\n", MAX);
	printf("MAX\n");

	return 0;
}

Graphic:
insert image description here
Conclusion:
The macro inside the string is actually a string.

space

# define MAX(a)++a
int main()
{
    
    

	int x = 0;
	printf("%d\n", MAX (x));

	return 0;
}

Parse:
insert image description here

Properly define statements with macros

wrong example

#define Init(a,b) a=100,b=200;
int main()
{
    
    

	int x = 0;
	int y = 1;
	if (1)
		Init(x, y);
	else
		x = 1;
	return 0;
}

Explanation:
The formula after precompilation:


int main()
{
    
    

	int x = 0;
	int y = 1;
	if (1)
		x=100,x=200;
		;
	else
		x = 1;
	return 0;
}

Explanation
if: Only one statement can be followed after no curly braces.
else matches the if of the most recent statement. That is, two statements cannot be separated.

Explanation: if is followed by two statements, one of which is an empty statement, and else only matches the if of the nearest statement, but there is an empty statement in between, so it cannot match.

Correct example:

#define Init(a,b) do{
      
      a=100,b=20;}while(0)
int main()
{
    
    
	int x = 0;
	int y = 1;

	if (1)
		Init(x, y);
	else
		x=1;
	
	return 0;
}

Code after precompilation:

int main()
{
    
    
	int x = 0;
	int y = 1;

	if (1)
		 do{
    
    x=100,y=20;}while(0);
	else
		x=1;
	return 0;
}
Explanation: do{}while(0); can contain multiple statements, and this loop will only be executed once. This perfectly solves the problem of defining multiple statements in a macro.

macro scope

We want to break a common sense:
Can macros only be defined outside functions?

Traditionally, we would think so, but macros can also be defined within the body of a function.

Look at this code:

int main()
{
    
    
#define MAX 100
	printf("%d\n", MAX);
#undef MAX
	return 0;
}

Explanation: #undef is to cancel the macro definition

Then we can determine the scope of the macro:
from the position where the macro is defined to the end of the cancellation of the macro, the scope of the macro is within this interval.
When the macro is not canceled, the scope is from the beginning of the definition to the end of the file.
Macros cannot be used across files when they are not included in the header file.

Look at the following piece of code:

int add(int x, int y)
{
    
    
#define MAX 100
	return x + y;
}
int main()
{
    
    

	printf("%d\n", MAX);
#undef MAX
	int x = 0;
	int y = 0;
	int ret = add(1, 2);
	printf("%d\n", MAX);
	return 0;
}

What we should think about is the time period for processing macros, and the way macros are processed:
preprocessing, replacement.

Try to think about the preprocessed code:

int add(int x, int y)
{
    
    
	return x + y;
}
int main()
{
    
    
	printf("%d\n", 100);
	int x = 0;
	int y = 0;
	int ret = add(1, 2);
	printf("%d\n", MAX);
	return 0;
}

Supplement: The last MAX is not in the scope of the macro, so the compiler will not recognize this MAX.
Misunderstanding: It is not to call the add function first and then recognize the macro.

Conclusion:
This code reports an error.

#and##

1.#

Function: Replace the parameters in the macro and convert them into strings

int main()
{
    
    
	printf("hello""world\n");
	return 0;
}

This shows that: the string has connection properties

The # attribute can change the above code to this:

#define X(a) #a
int main()
{
    
    
	printf("hello "X(world\n));
	return 0;
}

The code prints the same result!
Note:
The parameters of the macro are replaced , that is to say, no matter whether the passed parameters are meaningful or not, they will be converted into strings!

Function: Glue the symbols to form a new symbol ( must be legal )

#define STICK(a,b) a##b
int main()
{
    
    

	int beauty = 10;
	printf("%d", STICK(bea, uty));
	return 0;
}

Result: 10
We can even use it to define variable names

#define X(a) x##a
int main()
{
    
    
	int X(1)=10;//实际上是:int x1 = 10;
	printf("%d\n", X(1));
	return 0;
}

Suggestions for defining macros

1. Try to use curly braces to ensure the independence of macros
Counterexample:

#define MAX(a,b) a+b
int main()
{
    
    
	printf("%d\n", 2 * MAX(1, 2));
	//             2 *1 + 2= 3+2 = 5
	return 0;
}

Without parentheses, the result of the macro will be affected by the external parameters.

2. Avoid using operators with side effects such as ++ or –
insert image description here

The essence of macros is: replace

3. Macro definitions use all uppercase letters

This is our naming convention to distinguish macros from functions .

3. Conditional compilation

1. Constant expression

#if //常量表达式
//...
#endif
//常量表达式由预处理器求值。
//如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif

2. Multi-branch

#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif

3. Determine whether it is defined

#if defined(symbol)
//……
#endif
#ifdef symbol
//……
#endif
#if !defined(symbol)
//……
#endif
#ifndef symbol
//……
#endif

4. Nested instructions

#define A 100
#define B 200
#define C 300
#define D 400
#if defined(A)
	#ifdef B
		//……
	#endif
	#ifdef C
		//……
	#endif
#elif defined(D)
	#ifdef B
		//……
	#endif
#endif

Note:
The stage of conditional compilation processing: preprocessing stage

Conditional compilation statements all end with: #endif

Conditional compilation statement: can appear outside the body of the function

Adding a indicates the value of this macro

example:

#define A 0
int main()
{
    
    
#if A//这里实际上是0,所以此代码不执行
	printf("you can see me!\n");
#endif
	return 0;
}

Explanation: Conditional compilation can make one code multi-purpose , that is, a piece of code will be executed if the conditions are met in a suitable scene, and will not be executed if it is not satisfied, so that only one code needs to be maintained for different versions.

The header file avoids repeated inclusion methods: (compile with conditions)

#ifndef __TEST__
	#define __TEST
	//……代码块
#else
	//啥也不写
#endif

Explanation: The code needs to satisfy that __TEST__ is not defined when it is included for the first time, so we will define __TEST__ when it is included for the first time , and include the target code at the same time. The next time it is included, because __TEST__ has been defined, contains nothing.

Supplement: Header files can be included repeatedly, but repeated inclusion will make the code redundant .
Another way to avoid repeated inclusion of header files is: #pragma once

Four.include

#include is a preprocessing directive, which has been processed by the compiler during precompilation.

Here's an example of including a header file:

insert image description here

Summary: It is recommended that "" include the header files under your own project, and <> include the header files in the library .

Five.error and pragma

//#error 你好啊!
#pragma message(你好啊!)
int main()
{
    
    
	return 0;
}

The difference between these two instructions is: error is reported during compilation , and the instruction message is just a reminder
. The same thing: both let the compiler give you information.

Tip:
insert image description here
When we use the scanf function in VS, the compiler will report an error because this function is not safe. One solution is to define a macro

#define _CRT_SECURE_NO_WARNINGS 1

another:

#pragma warning(disable:4996)

predefined symbols

FILE //The source file for compilation
LINE //The current line number of the file
DATE //The date when the file was compiled
TIME //The time when the file was compiled
STDC //If the compiler follows ANSI C, its value is 1, otherwise it is undefined

Guess you like

Origin blog.csdn.net/Shun_Hua/article/details/129649361