C program environment and preprocessing

​​​​​​Article Directory

1. Program translation environment and execution environment

1. Program compilation process

2. Compile the internal principle

3. Execution environment

 2. Preprocessing before the program runs

1. Induction of predefined symbols

2.define definition identifier

3.define definition macro

4.define replacement rules

5. Comparison of macros and functions

Three, the way the header file is included 

4. Exercise: Write a macro that can swap the parity bits of an integer binary bit


  Hello everyone, I am Ji Ning.

  In any implementation of ANSI C, 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. There are also many internal principles in the translation environment, let's learn together.

1. Program translation environment and execution environment

1. Program compilation process

  

  Among them, the source file is the file with the file name suffix .c, and the target file is the file with the file suffix .obj.

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

2. Compile the internal principle

  In the preprocessing stage, the system will automatically process all precompiled instructions. Including deleting comments, including header files, #define defining symbols and replacing macros, etc.

  In the compilation stage, the system will translate the C language code into assembly instructions, as well as syntax analysis, word meaning analysis, semantic analysis, symbol summary, etc.

  at the compilation stage. The system translates assembly instructions into binary instructions and generates .o object files to form a symbol table.

  Finally, the segment table is merged in the linking stage, and the symbol table is relocated and synthesized ( in the linking stage, it can be found whether the function is defined normally )

3. Execution environment

  First, 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.

  The next step is to start the execution of the program, calling the main function to 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. Variables stored in static memory retain their values ​​throughout the execution of the program.

  The last is to terminate the program. The main function is terminated normally, or it may be terminated unexpectedly.

 2. Preprocessing before the program runs

1. Induction of predefined symbols

  The following symbols are built-in symbols of C language

__FILE__  source files for compilation
__LINE__  the current line number of the file
__DATE__  The date the file was compiled
__TIME__ The time the file was compiled

The corresponding symbols used in the program are automatically converted into corresponding meanings in the preprocessing stage. The test code is as follows

#include<stdio.h>
int main() {
    printf("%d\n", __LINE__);
    printf("%s\n", __TIME__);
    printf("%s\n", __DATE__);
    printf("%s\n", __FILE__);
    return 0;
}

operation result

  Since _LINE_ is on the fourth line, the conversion result is 4; the time and date are the time and date when I tested the code; _FILE_ corresponds to the path of my source file.

2.define definition identifier

The syntax is as follows:

#define name stuff

  In the preprocessing stage, the compiler will automatically replace all name in the code with stuff. However, it should be noted that when writing #define to define an identifier, you cannot add ; after it, and the compiler will use ; in the preprocessing stage as the content to be replaced.

3.define definition macro

  The #define mechanism includes a provision that allows parameters to be substituted into the text, and this implementation is often called a macro or define
macro. Here is how the macro is declared:

#define name( parament-list ) stuff

  The parament-list is a comma-separated list of symbols that may appear in stuff. Its use method is similar to that of a function, except that the function parameter is changed to a direct replacement. Because it has been changed to a direct replacement, the parameters of the macro should be modified with parentheses as much as possible . Second, the opening parenthesis of the parameter list must be immediately adjacent to name, because if there is any white space between the two, the parameter list will be interpreted as part of the stuff.

  Macro parameters are generally replaced many times, so those parameters with 'side effects' cannot be used. Side effects are permanent effects in expression evaluation, such as self-addition and self-subtraction operations.

  For example, find the operation result of the following code:

​#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);

  Most people may take it for granted that what is passed is the value of x++ and y++ expressions, then the structure should be the result of z should be 8, the result of x should be 6, and the result of y should be 9? But it's not:

  This is the effect of parameters with side effects 

4.define replacement rules

There are several steps involved when expanding #defines to define symbols and macros in a program.

1. When calling a macro, the parameters are first checked to see if they contain any symbols defined by #define. If yes, they are replaced first.

2. The replacement text is then inserted in the program in place of the original text. For macros, parameter names are replaced by their values.

3. Finally, the resulting file is scanned again to see if it contains any symbols defined by #define. If yes, repeat the above processing.

There are a few more points to note

1. Symbols defined by other #defines can appear in macro parameters and #define definitions. But with macros, recursion cannot occur .

2. When the preprocessor searches for symbols defined by #define, the content of the string constant is not searched

5. Comparison of macros and functions

  In some respects, macros are very advantageous. For example, a simple task such as finding the maximum value of two numbers may be very time-consuming if it is done with a function, because every time a function is called, the function stack frame must be created and destroyed, but the macro does not need it; Parameters must be of a specific type, but macro parameters can pass multiple types of parameters within the scope of arithmetic operations. The summary is: In small projects, macros are better than functions in terms of program size and speed ; macros are type-independent, and types can be passed as parameters, which functions cannot do .

  But macros also have the following disadvantages:

1. Every time a macro is used, a copy of the macro definition code will be inserted into the program. Unless the macro is relatively short, it can increase the length of the program considerably.

2. Macros cannot be debugged.

3. Since macros are type-independent, they are not rigorous enough.

4. Macros may cause operator priority issues, making programming prone to errors. 

Three, the way the header file is included 

  In C language, there are two ways to include header files: " "include and < >include.

  " "Inclusion is a way for the compiler to include local files. The search strategy is to 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 it is found If not, it will prompt a compilation error .

  The search header file contained in < > is directly searched in the standard path, and if it cannot be found, it will prompt a compilation error

  After seeing this situation, some people may ask: Can all files be included with " " in the future? The answer is yes, but if you consider the efficiency of the program, it is another matter, because " " to check The header file of the standard path needs to be searched twice, but the header file of the <> search for the standard path only needs to be searched once, and the efficiency is obviously much higher .

  In order to prevent header files from being repeatedly referenced, there will be conditionally compiled content in header files to prevent duplication of file content

Write these few statements at the beginning of each header file

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif  //__TEST_H_

or:

#pragma once

You can avoid repeated references to header files.

Here are some commonly used conditional compilation directives

#if
#elif
#endif

4. Exercise: Write a macro that can swap the parity bits of an integer binary bit

  Idea: take out the odd digit of the binary digit of this number, and move it one bit to the left; take out the even digit of the binary digit of this number, move it one bit to the right, and finally add it.

  Take 8-bit binary as an example: &0x55 can get its odd bit, &0xaa can get its even bit, if it rises to 32-bit binary, then &0x55555555 can get its odd bit, and then shift left, &0xaaaaaaaa can get its even bit , then right shift, and finally add to exchange the parity bits of the binary bits of this number.

  If you understand the diagram, it is very simple to write code and implement it with macros.

#include<stdio.h>
#define SWAP(n)  (n=((n&0xaaaaaaaa)>>1)+((n&0x55555555)<<1))
int main()
{
	int n = -120;
	SWAP(n);//交换奇偶位
	printf("%d\n", n);
	SWAP(n);//再交换回来
	printf("%d\n", n);
	return 0;
}

Use -120 to verify that the parity swap was successful

operation result:

  Time flies, and in the blink of an eye, the blog of C language is almost over, and some classic interview solutions of C language will be released later, please look forward to it. 

  In the next blog, I will start to learn and update the content of data structure and algorithm. Thank you for your support to Ji Ning.

Guess you like

Origin blog.csdn.net/zyb___/article/details/131870314