C language preprocessing commands (macro definition and conditional compilation)

C language preprocessing commands (macro definition and conditional compilation)

foreword

Before compiling and linking, it is necessary to perform some textual operations on the source file, such as text replacement, file inclusion, and deletion of some codes. This process is called preprocessing and is completed by the preprocessing program.
Compared with other programming languages, the C/C++ language relies more on the preprocessor, so in the process of reading or developing C/C++ programs, you may come into contact with a large number of preprocessing instructions, such as #include, #define, etc.

What are C language preprocessing commands?

C language source files must be compiled and linked to generate executable programs

  1. Compile (Compile) converts source files (.c files) into object files. For VC/VS, the object file suffix is ​​.obj; for GCC, the object file suffix is ​​.o.

Compilation is for a single source file, and one compilation operation can only compile one source file. If there are multiple source files in the program, multiple compilation operations are required.

  1. Linking (Link) is for multiple files, and it will combine multiple target files generated by compilation and libraries, components, etc. in the system into an executable program.

These simple processes of processing source files before compiling are called preprocessing (that is, preprocessing, advance processing).
Preprocessing is mainly to process commands beginning with #, such as #include <stdio.h>, etc. Preprocessing commands should be placed outside all functions, and generally placed in front of the source file.

Preprocessing is an important function of C language, which is completed by the preprocessing program. When compiling a source file, the system will automatically call the preprocessing program to process the preprocessing part of the source program, and automatically start compiling the source program after processing.

The compiler will save the preprocessing result to the .i file with the same name as the source file, for example, the preprocessing result of main.c is in main.i. Like .c, .i is also a text file, which can be opened with an editor to view the content directly.

C language provides a variety of preprocessing functions, such as macro definition, file inclusion, conditional compilation, etc. Reasonable use of them will make the written program easy to read, modify, transplant and debug, and is also conducive to modular programming design.

Example:

Let's take an example to illustrate the practical use of preprocessing commands. If you want to develop a C language program now, let it pause for 5 seconds before outputting content, and require cross-platform, running under both Windows and Linux, what should you do?

The difficulty of this program is that the suspend function and header files are different under different platforms: The
prototype of the suspend function under the Windows platform is void Sleep(DWORD dwMilliseconds) (note that S is capitalized), and the unit of the parameter is "milliseconds". Located in the <windows.h> header file.
The prototype of the suspend function under the Linux platform is unsigned int sleep (unsigned int seconds), and the parameter unit is "seconds", which is located in the <unistd.h> header file.

Different platforms must call different functions and import different header files, otherwise it will cause compilation errors, because there is no sleep() function and <unistd.h> header file under Windows platform, and vice versa. This requires us to solve this problem before compilation, that is, in the preprocessing stage. Please see the code below:

#include <stdio.h>
//不同的平台下引入不同的头文件
#if _WIN32  //识别windows平台
#include <windows.h>
#elif __linux__  //识别linux平台
#include <unistd.h>
#endif
int main() {
    
    
    //不同的平台下调用不同的函数
    #if _WIN32  //识别windows平台
    Sleep(5000);
    #elif __linux__  //识别linux平台
    sleep(5);
    #endif
    puts("http://c.biancheng.net/");
    return 0;
}

#if, #elif, #endif are preprocessing commands, they are all executed by the preprocessing program before compilation. Here we don't discuss the details, but only understand the whole.

For the Windows platform, the preprocessed code becomes:

#include <stdio.h>
#include <windows.h>
int main() {
    
    
    Sleep(5000);
    puts("http://c.biancheng.net/");
    return 0;
}

For the Linux platform, the preprocessed code becomes:

#include <stdio.h>
#include <unistd.h>
int main() {
    
    
    sleep(5);
    puts("http://c.biancheng.net/");
    return 0;
}

You see, under different platforms, the source code before compilation (after preprocessing) is different. This is the work of the preprocessing stage. It treats the code as ordinary text, performs some simple text replacements according to the set conditions, and then passes the results after the replacement to the compiler for processing.

Detailed usage of #include (file contains commands)

#include is called a file include command, which is used to introduce the corresponding header file (.h file). #include is also a type of C language preprocessing command.

The processing process of #include is very simple. It is to insert the content of the header file into the location of the command, so as to connect the header file and the current source file into one source file, which has the same effect as copying and pasting.

#include can be used in two ways, as follows:

#include <stdHeader.h>
#include "myHeader.h"

The difference between using angle brackets < > and double quotation marks " "is that the search path for header files is different:
using angle brackets < >, the compiler will search for header files in the system path;
while using double quotation marks " ", the compiler will first search in the current directory Search for the header file, if not found, then search in the system path.

In other words, using double quotes has one more search path than using angle brackets, and its function is more powerful.

We have been using angle brackets to introduce standard header files before, and now we can also use double quotes, as shown below:

#include "stdio.h"
#include "stdlib.h"

Both stdio.h and stdlib.h are standard header files, they are stored in the system path, so angle brackets and double quotes can be used to import them successfully; while the header files we write are generally stored in the path of the current project, so You cannot use angle brackets, only double quotes.

Usage of #define, C language macro definition

#define is called a macro definition command, which is also a kind of C language preprocessing command. The so-called macro definition is to use an identifier to represent a string. If the identifier appears in the following code, it will be replaced with the specified string.

Let's first look at the usage of #define through an example:

#include <stdio.h>
#define N 100
int main(){
    
    
    int sum = 20 + N;
    printf("%d\n", sum);
    return 0;
}

运行结果:
120

Note that the code on line 6 int sum = 20 + N, N is replaced by 100.

#define N 100 is the macro definition, N is the macro name, and 100 is the content of the macro (the string represented by the macro). In the preprocessing stage, for all "macro names" that appear in the program, the preprocessor will replace them with the strings in the macro definition, which is called "macro replacement" or "macro expansion".

The macro definition is completed by the macro definition command #define in the source program, and the macro replacement is completed by the preprocessing program.

The general form of a macro definition is:

#define  宏名  字符串

#Indicates that this is a preprocessing command, all preprocessing commands start with #. A macro name is a type of identifier, and the naming rules are the same as variables. Strings can be numbers, expressions, if statements, functions, etc.

The string mentioned here is a sequence of characters in the general sense, not the same as the string in C language, it does not need double quotes.

Expressions used repeatedly in the program can be defined using macros, for example:

#define M (n*n+3*n)

Its role is to specify the identifier M to represent the expression (y y+3 y). When writing codes, all occurrences of (y y+3 y) can be represented by M, and when compiling the source program, the preprocessor will first perform macro replacement, that is, use (y y+3 y ) to Replace all macro names M before compiling.

Complete the example above:

#include <stdio.h>
#define M (n*n+3*n)
int main(){
    
    
    int sum, n;
    printf("Input a number: ");
    scanf("%d", &n);
    sum = 3*M+4*M+5*M;
    printf("sum=%d\n", sum);
    return 0;
}
运行结果:
Input a number: 10
sum=1560

At the beginning of the program, a macro M is first defined, which represents the expression (n n+3 n). The macro M is used in 9 lines of code, and the preprocessor expands it to the following statement:

sum=3*(n*n+3*n)+4*(n*n+3*n)+5*(n*n+3*n);

It should be noted that in the macro definition, the parentheses on both sides of the expression (n n+3 n) must not be missing, otherwise ambiguity may arise after macro expansion. Here is a negative example:

#difine M n*n+3*n

After macro expansion, the following statement will be obtained:

s=3*n*n+3*n+4*n*n+3*n+5*n*n+3*n;

This is equivalent to:

3n2+3n+4n2+3n+5n2+3n

This is obviously incorrect. Therefore, care should be taken when defining macros, and it should be ensured that no ambiguity occurs after macro substitution.

A few notes on #define usage

  1. A macro definition uses the macro name to represent a string, and replaces the macro name with the string when the macro is expanded. This is just a simple and crude replacement. The string can contain any character, it can be a constant, expression, if statement, function, etc. The preprocessor does not check it. If there is an error, it can only be found when compiling the source program that has been expanded by the macro.

  2. A macro definition is not a statement or a statement, and there is no need to add a semicolon at the end of the line. If a semicolon is added, even the semicolon will be replaced together.

  3. The macro definition must be written outside the function, and its scope is from the macro definition command to the end of the source program. To terminate its scope, use the #undef command. For example:

#define PI 3.14159
int main(){
    
    
    // Code
    return 0;
}
#undef PI
void func(){
    
    
    // Code
}

Indicates that PI is only valid in the main() function, not in func().
4) If the macro name in the code is surrounded by quotation marks, the preprocessor will not replace it with a macro, for example:

#include <stdio.h>
#define OK 100
int main(){
    
    
    printf("OK\n");
    return 0;
}

Running result:
OK

In this example, the macro name OK is defined to represent 100, but OK is surrounded by quotation marks in the printf statement, so it is not replaced by a macro, but treated as a string.

  1. Macro definition allows nesting, the defined macro name can be used in the string of macro definition, and it will be replaced layer by layer by the preprocessor when the macro is expanded. For example:
#define PI 3.1415926
#define S PI*y*y    /* PI是已定义的宏名*/

pair statement:

printf("%f", S);

After macro substitution becomes:

printf("%f", 3.1415926*y*y);
  1. It is customary to use capital letters for macro names to distinguish them from variables. However, lowercase letters are also allowed.

  2. The data type can be represented by macro definition, which makes writing convenient. For example:

#define UINT unsigned int

UINT can be used as variable description in the program:

UINT a, b;

Note the difference between expressing a data type with a macro definition and defining a data specifier with a typedef. Macro definition is just a simple string replacement, which is processed by the preprocessor; while typedef is processed by the compiler at the compilation stage, it is not a simple string replacement, but a new name for the original data type, Make it a new data type.

See the example below:

#define PIN1 int *
typedef int *PIN2;  //也可以写作typedef int (*PIN2);

The two are similar in form, but not the same in practice.

The difference can be seen when PIN1 and PIN2 are used to explain the variables below:

PIN1 a, b;

After macro substitution becomes:

int * a, b;

Indicates that a is a pointer variable pointing to an integer, and b is an integer variable. However:

PIN2 a,b

Indicates that a and b are both pointer variables pointing to integers. Because PIN2 is a new, complete data type. It can be seen from this example that although the macro definition can also represent the data type, it is only a simple string replacement after all. Be extremely careful when using it to avoid mistakes.

Summary of preprocessing commands


The preprocessing function is a unique function of the C language. It is completed by the preprocessing program before the source program is officially compiled, and the programmer uses the preprocessing command to call these functions in the program.

The macro definition can have parameters, and when the macro is called, the actual parameters are substituted for the formal parameters, rather than "value transmission".

In order to avoid errors during macro substitution, brackets should be added to the character string in the macro definition, and brackets should be added to both sides of the formal parameters appearing in the string.

File inclusion is an important function of preprocessing. It can be used to connect multiple source files into one source file for compilation, and the result will generate an object file.

Conditional compilation allows compiling only the program segments that meet the conditions in the source program, making the generated target program shorter, thereby reducing memory overhead and improving program efficiency.

Using the preprocessing function facilitates the modification, reading, transplantation and debugging of the program, and also facilitates the realization of modular programming.

Reference article: C language Chinese website

Guess you like

Origin blog.csdn.net/weixin_45172119/article/details/130002205