当前位置:网站首页>C language - program compilation and execution, detailed macro definition

C language - program compilation and execution, detailed macro definition

2022-08-11 05:48:00 Yunyi 943

        Today I'm going to talk about how code is translated into a language that a computer can understand,The steps of the program running process and what is a macro,How is it defined?

目录

一.程序的翻译环境和执行环境

1.Source of translation and execution

2.翻译环境

2.1编译阶段:

2.2链接阶段: 

二.执行环境

1.Steps to execute the environment:

 三.预处理详解:

1.预定义符号

 2.#define定义标识符

        标识符声明:

3.#define 定义宏

                        #define name( parament-list ) stuff

4.An exercise in macro definition:

3. 嵌套宏定义

4. #define 替换规则

5.宏和函数的对比

6. 命名约定

7.#undef


一.程序的翻译环境和执行环境

1.Source of translation and execution

        在ANSI C的任何一种实现中,存在两个不同的环境. 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令. 第2种是执行环境,它用于实际执行代码.The combination of these two environments makes the source programtest.cThe file is converted into an executable program.exe文件.

2.翻译环境

The translation environment will be divided into two major stages:编译阶段和链接阶段.  

        

2.1编译阶段:

A total of translation phases need to be performed3个步骤:1.预处理     2.编译      3.汇编

test.c

#include <stdio.h>
int main()
{
  int g_val=100;
 printf("%d\n", g_val);
 print("hello bit.\n");
 return 0;
}

1.预处理过程:

A.#include头文件的包含————>关键语句(处理)

B.删除注释————>Use spaces instead of comments.

C.#define宏定义符号的替换————>The program replaces the macros in the code with constants,,Defining symbols are removed after replacement.

        in the preprocessing stage,The program will file the code we wrote(test.c )into a new file(test.i) 中,In addition, the header file written at the beginning of each time(例如:#include<stdio.h>Everything in this header file)will also be put in by the programtest.i中;The next step is to delete the comment,Comments are what programmers mark for themselves,Computers only need code,So will clean things up outside the code,course macrodefine也会被清理,defineThe replaced constants are factored out directly into the code中. 

2.编译过程:

        程序会把C代码翻译成汇编代码,Then do it in assembly code语法分析、词法分析、语义分析、符号汇总(main、函数名、全局变量等).test.i文件会转换为test.s文件.

3.汇编过程:

        The program converts the finished assembly code into binary instructions that the computer can understand,And form the symbol table of each segment.test.s文件——>test.obj文件.


2.2链接阶段: 

        当test.cThe resulting file after compilation is .obj目标文件,虽然目标文件的内容是本地代码,但是无法直接运行,原因就是:The current program is not yet completed.故.objA file is unfinished native code.So one or more.objThe file is finally converted to .exe文件.

        每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序. 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中.


二.执行环境

1.Steps to execute the environment:

1. 程序必须载入内存中.在有操作系统的环境中:一般这个由操作系统完成.在独立的环境中,程序 的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成.

2. 程序的执行便开始.接着便调用main函数. 

3. 开始执行程序代码.这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回 地址.程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值.

4. 终止程序.正常终止main函数;也有可能是意外终止.


 三.预处理详解:

1.预定义符号

__FILE__      //进行编译的源文件

__LINE__     //文件当前的行号

__DATE__    //文件被编译的日期

__TIME__    //文件被编译的时间

 The above preprocessing symbols can be used for logging:

int main() {
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL) {
		perror("fopen");
		return -1;
	}
	fprintf(pf,"file:%s\tline:%d\tdate:%s\ttime:%s\n", __FILE__, __LINE__, __DATE__, __TIME__);

	fprintf(pf, "file:%s\tline:%d\tdate:%s\ttime:%s\n", __FILE__, __LINE__, __DATE__, __TIME__);

	fprintf(pf, "file:%s\tline:%d\tdate:%s\ttime:%s\n", __FILE__, __LINE__, __DATE__, __TIME__);

	fclose(pf);
	pf = NULL;
	return 0;
}

 2.#define定义标识符

        标识符声明:

语法: #define name stuff

 name为宏名称,stuffis the value of the macro name,值可以是任何类型的数据,可以是表达式 、语句等.

3.#define 定义宏

         定义:#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏.The calculation of the macro definition is performed by the program in the preprocessing stage of the translation environment! 下面是宏的申明方式:

                        #define name( parament-list ) stuff

        参数详解:         

        parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中.

        注意: 参数列表的左括号必须与name紧邻. 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分.

                                A space is left,就表明(X)是属于stuff部分的了. 

4.An exercise in macro definition:

1.

#define QUARE(X) X*X

int main() {
	int r1 = QUARE(6);
	printf("%d\n", r1);

	int r2 = QUARE(6 + 2);
	printf("%d\n", r2);

        由上述代码可知,QUARE(X) X*X 是宏定义,意为求X的平方,而r1Assigned to be asked forQUARE(6),即6的平方,所以riThis statement can be converted to :int r1=6*6;     r1==36;

       而r2This statement appears to be a requirement6+2的平方赋值给r2,但结果为:20  如下图:

原因是:int r2= 6+2*6+2;6+2*6+2等于6+12+2等于20.This is due to symbol precedence,需要加括号,By improving the code below:

	
#define QUARE(X) X*X
#define QUARE2(X) ((X)*(X))
int main() {
//改进1:
	int r3 = QUARE2(6 + 2);
	printf("%d\n", r3);

	//改进2:
	int r4 = QUARE((6 + 2));
	printf("%d\n", r4);

    return 0;
    }

        The desired result can be obtained by improvement:


练习2. 

#define DOUBLE(X) X+X

int main() {
	int q = DOUBLE(3 * 2);
	printf("%d\n", q);

    int q1 = 10 * DOUBLE(3 * 2);
	printf("%d\n", q1);

    return 0;
    }

        Although this macro is forqThe operator does not require parentheses so strongly,Because the priority will be calculated first*号,再算+号,不影响计算;但对于q2parentheses are needed,如代码所示,I wanted to calculate10*DOUBLE,也就是10*(6+6)得120,But without parentheses it will be evaluated first10*6+6得出66.

        结论:

        所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用.

3. 嵌套宏定义

#define M 100
#define DOUBLE2(X) ((X)+(X))
int main() {
	int s = DOUBLE2(M+2);
	printf("%d\n", s);

    return 0;
    }

        The above code is a macro inside the macro definition,The process of calculation is also to replace symbols from the inside out://int s=((100+2)+(100+2))=204 .

        注:There are macros in the string that are useless!   The system thinks that everything in double quotes is just characters,It is not understood as a macro definition.


4. #define 替换规则

        在程序中扩展#define定义符号和宏时,需要涉及几个步骤:

        1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号.如果是,它们首先 被替换.

         2. 替换文本随后被插入到程序中原来文本的位置.对于宏,参数名被他们的值所替换.

         3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号.如果是,就重复上 述处理过程. 

练习:


int main() {
	int a = 10;
	printf("the value of a is %d\n", a);

	int b = 20;
	printf("the value of b is %d\n", b);
	return 0;
}

        在上述代码中,You need to create a variableprintf,If you build too many, it will cause code redundancy, At this time, everyone must think of functions:

void Print(int n) {
	printf("the value of n is %d\n", n);
}

         Although the function passes the formal parameters,But can't output with twoprintf一样的内容,And the type and format of the function are limited,The macro definition can be easily solved:

#define PRINT2(N,FORMAT) printf("the value of "#N" is "FORMAT"\n",N)

int main() {
	int a = 10;
    PRINT(a,"%d");    

	int b = 20;
	PRINT(b,"%d");
    
    float c = 3.14;
	PRINT(c,"%f");

	return 0;
}

 调试结果:

     Macros can actually ignore data types,As long as there is data coming in,It can perform the corresponding calculations;而函数做不到.


5.宏和函数的对比

代码1:

#define MAX(x,y) ((x)>(y)?(x):(y))

int max(int x, int y) {
	return x > y ? x : y;
}
int main() {
	int m = 10;
	int n = 15;
	int t = MAX(m, n);
	int t2 = max(m, n);
	return 0;
}

        上述代码中,Existing macros evaluate ternary expressions,There are also functions to evaluate ternary expressions,Which one is more convenient and faster?

        The answer is definitely macros.I said that when I first introduced the macro definitionThe calculation of the macro definition is performed by the program in the preprocessing stage of the translation environment!So macros are calculated during code preprocessing,Time spent is very short;The execution flow of the function call needs to pass parameters first,Function arguments are received,Begin to perform the internal operation of the function,Finally, return the value,This back and forth will take a lot of time,So macros take precedence over functions.

        而且,若m和nvalue is changed:

        The value returned by the function will be 3,The value derived from the macro definition is 3.95. 函数的参数必须声明为特定的类型, 所以函数只能在类型合适的表达式上使用.反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于>来比较的类型.This invisibly creates a sense of gap,Functions are less flexible than macros!

        其实,Macros are also a double-edged sword,There are highs and lows:

        1. 每次使用宏的时候,一份宏定义的代码将插入到程序中.除非宏比较短,否则可能大幅度增加程序的长度.

        2. 宏是没法调试的 .

        3. 宏由于类型无关,也就不够严谨.

        4. 宏可能会带来运算符优先级的问题,导致程容易出现错.

So in general,Functions and macros have their own advantages and disadvantages,So I looked up the data to summarize the advantages and disadvantages of many macros and functions:


6. 命名约定

        一般来讲函数的宏的使用语法很相似.所以语言本身没法帮我们区分二者. 那我们平时的一个习惯是:

把宏名全部大写

函数名不要全部大写


7.#undef

作用:这条指令用于移除一个宏定义. 

#define M 12345
int main() {
	int r = M;
	printf("%d\n", r);

#undef M
	int p = M;
	printf("%d\n", p);

	return 0;
}

    调试结果:

    r的值为12345,Macros removed,Mis an undeclared variable,pErrors will be reported when using variables!!!

原网站

版权声明
本文为[Yunyi 943]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/223/202208110512554238.html