Detailed explanation of C language preprocessing & program environment

Table of contents

First, the program environment

1. The translation environment of the program

Program compilation and linking

2. The operating environment of the program

Two, preprocessing instructions

         predefined symbols

1. #define

Usage 1: #define name stuff 

Usage 2: #define defines a macro

Error-prone points:

Comparison of pros and cons of macros and functions

 2. # and ## 

   # function

   ## Functions

 3. #undef:  

 4. Conditional compilation

 5. #include

 How to solve multiple inclusions of header files?


First, the program environment

        In any implementation of ANSIC , there are two distinct environments .
The first is the translation environment , where source code is converted into executable machine instructions.
The second is the execution environment , which is used to actually execute the code.

1. The translation environment of the program

Program compilation and linking

 The program is converted from source code that can be recognized by humans into a binary language that can be recognized by machines. The process is roughly as follows:

Among them, note: 

  • Each source file that makes up a program is converted into object code ( object code ) separately through the compilation process .
  • Each object file is bundled together by a linker ( linker ) to form a single and complete executable program.
  • The linker also imports any functions in the standard C library used by the program , and it can search the programmer's personal library and link the functions it needs into the program.

        

 The detailed steps and the functions of each step are shown in the figure:

Once we've translated into machine language, it's ready to run.

2. The operating environment of the program

The process of program execution
1. The program must be loaded into memory. In an environment with an operating system: Generally this is done by the operating system. In a standalone environment, program loading must be arranged manually, possibly by placing executable code into read-only memory.
2. The execution of the program starts. Then call the main function.
3. Start executing the program code. At this time, the program will use a runtime stack ( stack ) to store the local variables and return address of the function. Programs can also use static memory, and variables stored in static memory retain their values ​​throughout the execution of the program.
4. Terminate the program. Normal termination of the main function; unexpected termination is also possible.

Two, preprocessing instructions

 predefined symbols

predefined symbols
These predefined symbols are built-in to the language .
for example:
__FILE__       // 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 it is undefined

like: 

printf ( "file:%s line:%d\n" , __FILE__ , __LINE__ );
// The first one will print the path of the source file; the second one will print the line number where this code is located

1. #define

Here is the #define we are most familiar with,

Usage 1: #define name stuff 

#define k 20 // define a constant

(Note: Do not add a semicolon ";"    here , because during precompilation, stuff will replace all names in the program, so what you want is stuff, not stuff;)

Usage 2: #define defines a macro

Here is how the macro is declared:

       #define name( parament - list ) stuff where parament - list is a comma-separated list of symbols that may appear in stuff.

#define text(x)   2 * x

(Note: The #define mechanism includes a provision to allow arguments to be substituted into the text. This implementation is often called a macro or define macro.)

Error-prone points:

1. The left parenthesis of the parameter list must be adjacent to name . If any whitespace exists between the two, the argument list is interpreted as part of the stuff .
for example:
#define SQUARE( x ) x * x // correct way of writing
2. Try to wrap the replacement target in parentheses to avoid errors in the order of operations.
#define text(x, y) x + y // High-risk code, very error-prone
// For example: printf("%d", 2 * text(2, 3)); // The output is: 7 and what you want is 10, so the correct writing should be
#define text(x, y)     ((x) + (y)) 
//  So macro definitions used to evaluate numeric expressions should be parenthesized in this way to avoid the operator or parameter in the macro when using the macro
Unexpected interactions between proximity operators .
Notice:
1. Other variables defined by #define can appear in macro parameters and #define definitions. But with macros, recursion cannot occur .
2. When the preprocessor searches for symbols defined by #defifine , the content of the string constant is not searched .

Comparison of pros and cons of macros and functions

(Note: widescreen viewing is recommended )

Attribute                                              #define defines the macro                                                                function
Code Length Macro code is inserted into the program each time it is used. Except for very small macros,      every time this function is used ,                          the length of the program will be greatly increased by calling the same code at that place; the function code only appears in one place;
Execution speed Faster There is additional overhead for function calls and returns, so it is relatively slow
Operator precedence Macro parameters are evaluated in the context of all surrounding expressions, function parameters are only evaluated once when the function is called, and its resulting value is passed
                              Unless parenthesized, the precedence of the proximity operator may be given to the function. The result of evaluating an expression is more predictable. 
                              Unforeseen consequences, so it is recommended to have more parentheses when writing macros.                                                              
Parameters with side effects may be replaced in multiple positions in the macro body, so parameter function parameters with side effects are only evaluated once when passing parameters, and the result is easier to control.
 Evaluation of the number of arguments may produce unpredictable results.
Parameter type The parameters of the macro have nothing to do with the type. As long as the operation on the parameters is legal, it can be used. The                  parameters of the function are related to the type. If the types of the parameters different, different functions are required, even if the tasks they perform are different. of.
                                                                                                                                       to be used for any parameter type.
Debugging Macros are inconvenient to debug, functions can be debugged statement by statement                                                    
Recursion Macros cannot be recursive Functions can be recursive

2. # and ## 

Thinking: How to insert parameters into the string?

# function

First let's look at this code:

char* p = "hello ""bit\n";
    printf("hello ""bit\n");
    printf("%s\n", p);

 From the results, we will find that the hello bit is printed. We found that strings are characterized by automatic connection .

   Another trick is: Use # to turn a macro parameter into a corresponding string .

for example:

The first one: #define text(x) printf("I " "x" "China\n"); //parameter x needs to be a string

The second type: #define text(x) printf("I "#x"China\n"); // if x is also a string

## Functions:

   ## You can combine the symbols on both sides of it into one symbol. It allows macro definitions to create identifiers from separated text fragments.
#define ADD_TO_SUM(num, value)
sum##num += value;
...
ADD_TO_SUM(5, 10);// The function is: add 10 to sum5 .
Note: Such a connection must result in a valid identifier. Otherwise the result is undefined.

 3. #undef:  

This command is used to remove a macro definition.
#undef NAME //If an existing name needs to be redefined, its old name must be removed first.

4. Conditional compilation

For example:
         For debugging code, it is a pity to delete it, but to keep it in the way, so we can selectively compile it.
When compiling a program, it is very convenient if we want to compile or discard a statement (a group of statements). Because we have conditional compilation directives. (It is widely used to modify the library at the bottom layer)
like:
#include <stdio.h>
#define max 2
int main()
{
#if max == 1
	printf("hehe\n");  // 不编译
#elif max == 2
	printf("baba\n");  // 编译
#else
	printf("gungun\n");  // 不编译
#endif // 1
	return 0;
}

5. #include

    We already know that the #include directive can cause another file to be compiled. Just like where it actually appears in the #include directive.
The way this replacement works is simple: the preprocessor first removes the directive and replaces it with the contents of the include file . Such a source file is included 10 times, it is actually compiled 10 times. (Multiple inclusions are a waste of resources)
    We will notice:
#include  <stdio.h>   
#include "text.h" // self-built header file 

There are two types of "" and <>, which are actually two search strategies :

  •  ""    : First search in the directory where the source file is located . If the header file is not found, the compiler will search for the header file in the standard location just like searching for the library function header file. If not found, a compilation error will be prompted.
  • <> :    Find the header file and go to the standard path to search directly. If it cannot find it, it will prompt a compilation error. (The installation path that comes with the library file)

In this way, can it be said that the form of "" can also be used for library files? The answer is yes, yes, but the efficiency of searching in this way is lower. Of course, it is not easy to distinguish whether it is a library file or a local file.

 How to solve multiple inclusions of header files?

Solution:

  • Advanced: add #pragma once at the beginning of the header file

 #pragma once

  • Add manually: at the beginning of the header file
#ifndef _text_h_   // 没定义 返回1;否则返回0,结束编译
#define _text_h_

// ....... 各种包含头文件,函数声明
int k(int x, int y);
// .......

#endif // !_text_h_

Guess you like

Origin blog.csdn.net/qq_72112924/article/details/130206468