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

Article directory

  • foreword
  • 1. Pretreatment
    • head File
    • Macros and Comments
  • 2. Explain the macro in detail
    • note
    • Can macros be used in strings?
    • space
    • Properly define statements with macros
    • macro scope
    • #and##
        • 1.#
    • Suggestions for defining macros
  • 3. Conditional compilation
      • 1. Constant expression
      • 2. Multi-branch
      • 3. Determine whether it is defined
      • 4. Nested instructions
  • Four.include
  • Five.error and pragma
    • predefined symbols

Foreword

First come to two pictures to review the basic content of preprocessing

Combining these two graphs we get

1. Each source file needs to be compiled separately by a 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. An executable program is actually a text file that can run.

And the new question we need to ask is

1. What exactly does the header file contain? 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. Preprocessing

header 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


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 file, function declaration, macro definition, preprocessing directive, 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 is only the declaration of the function, Then 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 target file and the link library, and other links do not include external files, so the answer comes out naturally,

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 (feature). 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\\
");
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 execute.

result:
Delete the comment first, and the printf content will be executed.

picture:

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

2. Detailed macro

Comment

#define A /*
#define B */

Analysis:
/*First match with */, so /*#define B */ is the comment part.

Can macros be used in strings?

#define MAX 100
int main()
{<!-- -->

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

return 0;
}

Illustration:

in conclusion:
The macro inside the string is actually a string.

Space

# define MAX(a) + + a
int main()
{<!-- -->

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

return 0;
}

Parse:

Correctly use macro definition statements

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;
}

explain:
The precompiled formula:

int main()
{<!-- -->

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

illustrate
if: no curly braces can be followed by only one statement.
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;
\t
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.

Scope of the macro

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

Conventionally, we think it is, but macros can also be defined in the function body.

Look at this code:

int main()
{<!-- -->
#define MAX 100
printf("%d\\
", 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 canceling the macro, this section is the scope of the macro.
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\\
", MAX);
#undef MAX
int x = 0;
int y = 0;
int ret = add(1, 2);
printf("%d\\
", MAX);
return 0;
}

What we should think about is the time period for processing macros, and the processing method of macros:
Preprocessing, replacement.

Try to think about the preprocessed code:

int add(int x, int y)
{<!-- -->
return x + y;
}
int main()
{<!-- -->
printf("%d\\
", 100);
int x = 0;
int y = 0;
int ret = add(1, 2);
printf("%d\\
", 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.

in 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\\
");
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\\
));
return 0;
}

The code prints the same result!
Notice:
The parameters of the macro are replaced, that is to say, no matter whether there are parameters passed Meaning, will be converted to a string!

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 define variable names with

#define X(a) x##a
int main()
{<!-- -->
int X(1)=10;//actually: int x1 = 10;
printf("%d\\
", X(1));
return 0;
}

Suggestions for defining macros

1. Try to use braces to ensure the independence of macros
Counter example:

#define MAX(a,b) a + b
int main()
{<!-- -->
printf("%d\\
", 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 –

The essence of the macro 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 //constant expression
//...
#endif
// Constant expressions are evaluated by the preprocessor.
//like:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif

2. Multi-branch

#if constant expression
//...
#elif constant expression
//...
#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

Notice:
Stages of conditional compilation processing: preprocessing stage

All conditional compilation statements end with: #endif

Conditional compilation statement: can appear outside the function body

Add after #if A macro represents the value of this macro

example:

#define A 0
int main()
{<!-- -->
#if A//This is actually 0, so this code is not executed
printf("you can see me!\\
");
#endif
return 0;
}

Description: Conditional compilation can allow one code to be used for multiple purposes, that is, a piece of code will be executed when the conditions are met in a suitable scene, and will not be executed if the conditions are not met. In this way, only one code needs to be maintained for different versions.

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

#ifndef __TEST__
#define __TEST
//...code block
#else
// write nothing
#endif

Explanation: When the code is included for the first time, it needs to meet __TEST__ is not defined, so we When it is included once, __TEST__ will be defined, and the target code will be included at the same time, and the next time it will be included, since __TEST__ has already been defined, nothing will be included.

Supplement: Header files can be included repeatedly, but repeated inclusion will make 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:

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

5.error and pragma

//#error Hello!
#pragma message(Hello!)
int main()
{<!-- -->
return 0;
}

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

Skill:

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 the file was compiled
TIME //The time when the file was compiled
STDC //If the compiler follows ANSI C, its value is 1, otherwise undefined

syntaxbug.com © 2021 All Rights Reserved.