Program environment and preprocessing

catalogue

Program translation environment and execution environment

Compilation and linking

Pretreatment

Program translation environment and execution environment

In any implementation of ANSI C, there are two different environments. Translation environment and execution environment.

Translation environment: in this environment, the source code is converted into executable machine instructions.

Execution environment: used for the execution of actual code.

The source file forms the target file through the compiler. Object files and link libraries form executable programs through connectors.

Each source file constituting a program is converted into object code through the compilation process.

Each object file is bound together by a linker to form a single and complete executable program.

The link library will also introduce any function 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 to the program.

Compile and link

The compilation is divided into three parts: pre compilation, compilation and assembly

Precompiled: suffix to C file conversion to I file. The main operations are text operations such as including header file, deleting or replacing comments with blank space, #define defined symbol replacement, etc. (gcc -E test.c -o test.i)

Compile: suffix to I file conversion to S file. Convert c language code into assembly code. Including syntax analysis, lexical analysis, semantic analysis and symbol summary. (gcc -S test.i ---> test.s)

Assembly: suffix to S file to O documentation. Converts assembly code into binary instructions. Form a symbol table. (gcc -c test.s -->test.o)

Link: Yes, multiple links O file link is an executable program, including merging segment table, merging and relocation of symbol table. (gcc test.o -o test)

See what happens at each step during compilation:

1. Preprocessing option GCC - E test c -o test.i. Stop after the preprocessing is completed, and the results generated by the preprocessing are placed in test I file.

2. Compile option GCC - s test C, stop after compilation, and save the results in test S.

3. Assembly option GCC - C test C stop after the assembly is completed, and the results are saved in test O medium

Process of program execution:

1. The program must be loaded into memory.

In an environment with an operating system: it is generally completed by this operating system.

In an independent environment, the loading of the program must be arranged manually, or it may be completed by putting the executable code into the read-only memory.

2. The execution of the procedure begins. Then call the main function.

3. Start executing program code. At this time, the program will use a runtime stack to store the local variables and return addresses of the function. Programs can also use static memory storage. Variables stored in static memory maintain their values throughout the execution of the program.

4. Terminate the procedure. Normally terminate the main function; It may also terminate unexpectedly.

Pretreatment

Predefined symbols: defined symbols are built-in by the language

	__FILE__; // Currently compiled source file
	__LINE__; // The current line number of the file
	__DATE__; // Date the file was compiled
	__TIME__; // The time when the file was compiled
	__STDC__; // If the compiler follows ANSI C (c99), its value is 1, otherwise it is not defined 

#define defined identifier

Syntax:

#define name staff
// Define name as staff
#include <stdio.h>
#define MAX 100 / / define max as 100
#define reg register / / create a short name for the keyword register
#define do_fover for(; ;)      //  Replace the implementation with visual symbols
#define CASE break;case / / break is automatically added every time a case is written

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

#define defined macro

#The define mechanism includes a provision that allows parameters to be replaced into text. This implementation is usually called a macro or definition macro.

Macro definitions used to evaluate numeric expressions should be bracketed to avoid unexpected interactions between operators in parameters or adjacent operators when using macros.

Macro declaration method:

#define	name(parament-lise) stuff
// The left parenthesis must be immediately adjacent to name
#include <stdio.h>
#define SUM(x, y) x + y
int main()
{
	printf("%d\n", SUM(2, 3)); // SUM(2, 3) == 2 + 3
	printf("%d\n", 10 * SUM(2, 3)); // 10 * SUM(2, 3) == 10 * 2 + 3 = 23
	return 0;
}

#include <stdio.h>
#define SUM(x, y) ((x) + (y))
int main()
{
	printf("%d\n", SUM(2, 3)); // SUM(2, 3) == 2 + 3
	printf("%d\n", 10 * SUM(2, 3)); // 10 * SUM(2, 3) == 10 * ((2) + (3)) = 50
	return 0;
}
// Macro defined by #define with parentheses

#Replace rule of define:

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

2. The replacement text is then inserted into the position of the original text in the program. For macros, the parameter names are replaced by them.

3. Finally, scan the result file again to see if it contains any symbols defined by #define. If yes, repeat the above process.

Note:

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

2. When the preprocessor searches #define defined symbols, the contents of string constants are not searched.

# and##

#: change a macro parameter into the corresponding character.

#include <stdio.h>
#define PRINT(n) printf("the value of "#n" is %d\n", n)

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	PRINT(a);
	PRINT(b);
	PRINT(c);
	return 0;
}

#include <stdio.h>
#define PRINT(FORMAT, VALUE) printf("the value of " #VALUE " is " FORMAT "\n", VALUE)

int main()
{
	int a = 10;
	char b = 'a';
	PRINT("%d", a);
	PRINT("%d", a + 2);
	PRINT("%c", b);
	return 0;
}

##: combine the symbols on both sides into one symbol. Allows macro definitions to create identifiers from separate text fragments.

Must be a legal identifier, otherwise the result is undefined.

#include <stdio.h>
#define CAT(C, NUM) C##NUM

int main()
{
	int twentyone = 21;
	printf("%d\n", CAT(twenty, one));
	return 0;
}

Macro parameters with side effects

When the macro parameter appears more than once in the macro definition, if the parameter has side effects, you may be in danger when using this macro, resulting in unpredictable consequences. A side effect is a permanent effect when the expression is evaluated.

#include <stdio.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main()
{
	int x = 2;
	int y = 3;
	int z = MAX(x++, y++);
	// (2++) > (3++)    ❌
	//  x 3      y 4
	//            4++
	//  x = 3, y = 5, z = 4
	printf("x = %d\ny = %d\nz = %d\n", x, y, z);
	return 0;
}

Operation results:

Comparison of macros and functions

Advantages of macros over functions:

1. The code used to call and return functions may take more time than actually executing this small computing project. So macros are better than functions in terms of program size and speed.

2. The parameter of the function must be declared as a specific type. Therefore, functions can only be used on expressions of appropriate types. Conversely, this macro can be applied to integer, long integer, floating point and other types. Macros are type independent.

Disadvantages of macros over functions:

1. Every time a macro is used, a copy of the code defined by the macro will be inserted into the program. Unless the macro is relatively short, the length of the program may be greatly increased.

2. There is no way to debug macros.

3. Macro is not rigorous enough because it is independent of type.

4. Macros may cause operator priority problems, resulting in program errors

A comparison between macros and functions
attribute#define macrofunction
Code lengthMacro code is inserted into the program every time it is used. In addition to handling very small macros, the length of the program increases significantlyFunction code only appears in one place; Every time this function is used, the same code in that place will be called
Execution speedFasterThere is additional overhead for function calls and returns
operator precedence The evaluation of macro parameters is to add parentheses to the division in the context of all surrounding expressions, otherwise the priority of adjacent operators may have unpredictable consequences. Therefore, it is recommended that macros be written with more parenthesesThe function parameter is evaluated only once when the function is called, and its result value is passed to the function. The evaluation result of the expression is easier to predict.
Parameters with side effectsParameters can be replaced to multiple locations in the macro body, so parameter evaluation with side effects may have unpredictable consequences.Function parameters are evaluated only once when passing parameters, and the result is easier to control
Type of parameterThe parameters of a macro have nothing to do with the type. As long as the operation on the parameters is legal, it can be applied to any parameter typeThe parameters of a function are related to the type. If the types of parameters are different, different functions are required, even if they perform the same task
debuggingMacros are inconvenient to debugFunctions can be debugged statement by statement
recursionMacros cannot be recursiveFunctions are recursive

Naming convention:

Macro all uppercase

Function names should not be capitalized

#undef: used to remove a macro definition

#include <stdio.h>

#define MAX(x, y) ((x) > (y) ? (x) : (y))
int main()
{
	int m = MAX(2, 3);
	printf("%d\n", m);
	return 0;
}

#include <stdio.h>

#define MAX(x, y) ((x) > (y) ? (x) : (y))
int main()
{
#undef MAX
	int m = MAX(2, 3);
	printf("%d\n", m);
	return 0;
}

 

Command line definition: allows you to define symbols on the command line to start the compilation process.

When we compile different versions of a program according to the same source file. For example: determine different array sizes in different machine bits.

#include <stdio.h>

int main()
{
	int arr[ARRAY_SIZE];// You can use command-line parameters to assign the size of the array during compilation
	for (int i = 0; i < ARRAY_SIZE; i++) {
		arr[i] = i;
	}
	for (int i = 0; i < ARRAY_SIZE; i++) {
		printf("%d ", arr[i]);
	}
	return 0;
}

Conditional compilation: when compiling a program, compile or discard a statement.

Common conditional compilation instructions:

1.

#if constant expression

//

#endif

2. Conditional compilation of multiple branches

#if constant expression

// ..

#elif constant expression

// ..

#else constant expression

// ..

#endif

3. Judge whether it is defined

#if defined(symbol)

#ifdef symbol

#if !defined(symbol)

#ifndef symbol

4. Nested instructions:

#if defined(OS_UNIX)

        #ifdef OPTION1

                unix_version_option1();

        #endif

        #ifdef OPTION2

                unix_version_option2();

        #endif

#elif defined(OS_MSDOS)

        #ifdef OPTION2

                msdos_version_ option2();

        #endif

#endif       

#include <stdio.h>

#define M 0
int main()
{
	int i = 0;
	int n = 10;
	for (i = 0; i < 10; i++) {
#if M
		printf("%d ", i);
#endif
		printf("1 ");
	}
	return 0;
}

#include <stdio.h>

#define M 2
int main()
{
	int i = 0;
	int n = 10;
	for (i = 0; i < 10; i++) {
#if M
		printf("%d ", i);
#endif
		printf("1 ");
	}
	return 0;
}

 

#include <stdio.h>

#define M 150
int main()
{
#if M < 100
	printf("less\n");
#elif M == 100
	printf("equal\n");
#elif M > 100 && M < 200
	printf("middle\n");
#else
	printf("more\n");
#endif
	return 0;
}

#include <stdio.h>
#define M 0
int main()
{
#ifdef M
	printf("Mdefine1\n");
#endif

#if defined(M)
	printf("Mdefine2\n");
#endif

#ifndef M
	printf("Mnodef1\n");
#endif

#if !defined(M)
	printf("Mnodef2\n");
#endif
	return 0;
}

#include <stdio.h>

int main()
{
#ifdef M
	printf("Mdefine1\n");
#endif

#if defined(M)
	printf("Mdefine2\n");
#endif

#ifndef M
	printf("Mnodef1\n");
#endif

#if !defined(M)
	printf("Mnodef2\n");
#endif
	return 0;
}

 

How to include header files:

Local file contains: use double quotation marks

Search strategy: now search under the directory where the source file is located. If the header file is not found, the compiler will find the header file in the standard location.

Including library files: use < >

Search strategy: directly search under the standard path. If it cannot be found, an error is returned

Prevent a file from being contained more than once:

Write in each header file

1.

#ifdef __TEST_H__

#deine __TEST_H_

//

#endif

2.

#pragma once

Keywords: C C++ Back-end

Added by Negligence on Sat, 26 Feb 2022 21:55:17 +0200