Program environment and preprocessing (on) - "C"

Hello everyone uu from CSDN, today the content of Xiaoyalan is the knowledge point of program environment and preprocessing in C language. This knowledge point is the last knowledge point of C language in Xiaoyalan. Update the reading of some C language books, such as: "C Primer Plus" and "C Language Depth Analysis". Alright, let's get into the world of procedural environments and preprocessing.


The translation environment and execution environment of the program

Detailed compilation + link

Detailed preprocessing


The translation environment and execution environment of the program

 We have already understood it before: a test.c file is compiled and linked to generate an executable program .

However, in this process, what the specific process is like, with our current knowledge, we have no way of knowing, so, in this blog, let Xiao Yalan take you to learn this process together! ! !

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.

Translation environment including compiler and linker 

In VS, the compiler is called cl.exe, and the linker is called link.exe


Detailed compilation + link 

translation environment

In a project, there may be multiple .c and .h

 For example: contact.h contact.c test.c

Contents of add.c:

#define _CRT_SECURE_NO_WARNINGS 1
int add(int x, int y)
{
	return x + y;
}

 Contents of test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//声明
extern int add(int x,int y);
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int c = add(a, b);
	printf("%d\n", c);
	return 0;
}

 

  

 

The use of VScode - "VSCode" 

instruction:

 

You will find that there are many codes

This is the content of the header file stdio.h included in #include

 

 

 

 

Let's take a look: what is the symbol summary, what is the formation of the symbol table, the merging and relocation of the symbol table

 

  

Merging of symbol tables 

 

 Let's look at an example of symbol table merging:

  • 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 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 compilation itself is also divided into several stages:

Look at the code:

sum.c

int g_val = 2016;
void print(const char *str)
{
 printf("%s\n", str);
}

 test.c

#include <stdio.h>
int main()
{
 extern void print(char *str);
 extern int g_val;
 printf("%d\n", g_val);
 print("hello bit.\n");
 return 0;
}

 

1. Preprocessing option gcc -E test.c -o test.i

Stop after the preprocessing is completed, and the results generated after the preprocessing are placed in the test.i file.

2. Compile option gcc -S test.c

Stop after the compilation is complete, and the result is saved in test.s.

3. Compile gcc -c test.c

After the compilation is completed, it stops, and the result is saved in test.o.

operating environment

The process of program execution:

  • 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.
  • Program execution begins. Then call the main function.
  • Start executing 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.
  • Terminate the program. Normal termination of the main function; unexpected termination is also possible.

 The runtime stack is the function stack frame! ! !


 Detailed preprocessing

predefined symbols

__FILE__      //进行编译的源文件

__LINE__     //文件当前的行号

__DATE__    //文件被编译的日期

__TIME__    //文件被编译的时间

__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

 These predefined symbols are built-in to the language.

Let's look at an example:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int i = 0;
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	for (i = 0; i < 10; i++)
	{
		printf("%d----%s,%s,%s,line=%d\n", arr[i], __FILE__, __DATE__, __TIME__, __LINE__);
	}
	return 0;
}

Explain that VS does not strictly follow the C language standard

 In fact, when the compiler compiles the code, it will rename the function and variable names.

In C language, the rule of renaming is basically: add _

It will be more complicated in C++

#define

#define define identifier

grammar:

#define name stuff

Take a chestnut:

#define MAX 1000
#define reg register          //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)     //用更形象的符号来替换一种实现
#define CASE break;case        //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:% s\ttime : % s\n" ,\
                                 __FILE__, __LINE__, \
                                 __DATE__, __TIME__)

 When defining the identifier, do you want to add it at the end; 

 for example:

#define MAX 1000;
#define MAX 1000

It is not recommended to add ; , which can easily lead to problems.

For example, the following scenario:

if (condition)
  max = MAX;
else
  max = 0;

There will be a syntax error here.

#define Defining macros

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.

Notice:

The opening parenthesis of the parameter list must be immediately adjacent to name.

If any whitespace exists between the two, the argument list is interpreted as part of the stuff.

like:

#define SQUARE( x ) x * x

This macro receives a parameter x, if after the above declaration, you put

SQUARE( 5 );

Placed in a program, the preprocessor will replace the above expression with the following expression:

5 * 5

warn:

There is a problem with this macro:

Observe the code snippet below:

int a = 5;
printf("%d\n" ,SQUARE( a + 1) );

At first glance, you might think that this code will print the value 36.

In fact, it will print 11.

Why?

When replacing text, the parameter x is replaced with a + 1, so this statement actually becomes:

printf ("%d\n",a + 1 * a + 1 );

This makes it clear that the expressions produced by the substitution are not evaluated in the expected order.

Adding two parentheses to the macro definition solves this problem easily:

#define SQUARE(x) (x) * (x)

After preprocessing in this way, the expected effect is produced:

printf ("%d\n",(a + 1) * (a + 1) );

Here is another macro definition:

#define DOUBLE(x) (x) + (x)

We used parentheses in the definition to avoid the previous problem, but this macro may have new errors.

int a = 5;
printf("%d\n" ,10 * DOUBLE(a));

What value will this print?

It looks like it prints 100, but in fact it prints 55.

We find that after replacing:

printf ("%d\n",10 * (5) + (5));

The multiplication operation precedes the addition defined by the macro, so 55 appears.

The solution to this problem is to add a pair of parentheses around the macro definition expression.

#define DOUBLE( x)   ( ( x ) + ( x ) )

hint:

All macro definitions used to evaluate numeric expressions should be parenthesized in this way to avoid unforeseen interactions between operators in arguments or adjacent operators when using the macro.

#define Substitution Rules

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

  • When calling a macro, the arguments are first checked to see if they contain any symbols defined by #define. If yes, they are replaced first.
  • The replacement text is then inserted into the program in place of the original text. For macros, parameter names are replaced by their values.
  • Finally, the resulting file is scanned again to see if it contains any symbols defined by #define. If yes, repeat the above processing.

Notice:

  • Other #define-defined symbols can appear in macro parameters and #define definitions. But with macros, recursion cannot occur.
  • When the preprocessor searches for symbols defined by #define, the contents of string constants are not searched.

Well, everyone, today’s knowledge points are here. In fact, these knowledge points are quite abstract, especially in the compilation area. The remaining preprocessing knowledge points will be shared by Xiao Yalan in the next blog. Keep going! ! !

 

 

Guess you like

Origin blog.csdn.net/weixin_74957752/article/details/130046094