编译原理

编译原理之预编译处理

预编译处理是在编译器编译之前做的处理,预编译过程主要处理规则如下:

1、将所有的#define删除,并且展开所有的宏定义;

2、处理所有的条件编译指令,如“#if”,“ifdef”,“endif”等;

3、处理所有包含指令“#include”,将被包含的文件插入到该编译指令的位置;

4、删除所有注释“//”,“/**/”;

5、添加行号和文件名标识,方便编译时调试方便查找信息;

6、保留所有的#pragma编译器指令,因为编译器须要使用它们。

总的来说C语言提供的预编译功能主要有三种:

第一:宏定义

第二:文件包含

第三:条件编译

宏定义

宏定义分两种:不带参数和带参数的宏定义,宏定义总的来说就是符号之间简单的替换。

第一:不带参数宏定义

不带参数的宏定义比较好理解,就是用一个指定的标识符来代替一个字符串,一般形式为:

#define 标识符   字符串

例如:#define   TODAY   20140701

这个宏定义就是简单的用TODAY代替20140701这个字符串,程序预编译之后,程序中所有出现TODAY的地方都是指20140701。

与#define命令相对应的 #undef命令,#undef命令是用来终止宏定义的作用域。通常这个命令编程时用的比较少。例如:

#define MCU  8051

Main()

{

`````````````````;

}

#undef  MCU

Void Metal()

{;}

// #undef  MCU  命令以上部分程序出现的MCU都是指定为8051,但是如果#undef   MCU 命令下面程序出现MCU,编译器编译时会报错,提示MCU没定义。例如,Metal()函数中出现MCU就会报错。

 

第二:带参数宏定义

带参数的宏定义的一般形式为:

#define 宏名(参数表)  字符串

如:

#define S(a,b)  a*b

temp = S(2,3);这句语句展开为:temp = 2*3;

 

宏定义要注意的地方:

1、宏定义#define是预编译指令而不是语句,所以define紧跟着的字符串后面不能加分号“;”, 否则,分号也属于字符串的内容。例如:#define  TODAY   20140701;则这里的字符串内容是20140701;而不是20140701。

2、宏定义#define命令式用宏名代替一个字符串,就是一个简单的置换,不做任何的词法、语法的判断和计算,不会提示错误信息。

3、带参数宏定义时,宏名与带参数的括号之间不能加空格。否则将空格以后的字符都作为代替字符串的一部分,而变成不带参数的宏定义。

4、宏定义中如果参数表的实参是表达式时就要注意。如:#define S(r)  PI*r*r

则S(a) 在程序中的展开式为PI*a*a,这个很好理解;但是S(a+b)展开式为PI*a+b*a+b;显然这不是我们所想得到的结果。如想到PI*(a+b)*(a+b)这个结果,那么宏定义应为:

#define S(r)  PI*(r)*(r) 。这也体现了宏定义只是简单愚蠢直接替代功能。

 

带参数宏定义与函数的区别:

1、函数条用时,线求出实参表达式的值,然后代入形参;而宏定义只是简单的字符替换,不计算。

2、函数条用是在程序运行处理的,为形参分配临时内存单元;而宏展开式在编译前进行的,在展开时不分配内存单元,不进行值的传递。

3、函数的形参和实参的类型有要求,都要相同;宏不存在类型问题,宏的参数只是一个字符代表,展开时只是简单的替换。

4、调用函数只可得到一个返回值,而用宏可以设法得到几个结果。

   例如:#define several(a,b,c,d)  b = 2*a;c = 3*b;d = 4*c

5、宏替换不占运行时间,只占据编译的时间;而函数调用则占用运行时间。

 

文件包含

“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含本文件中。其一般形式为:

#include“文件名”或者#include <文件名>

这两种的区别只是寻找包含文件的路径不同而已。用<文件名>形式时,系统到存放C库函数头文件所在的目录中寻找要包含的文件,这称为标准方式。用“文件名”方式时,系统线在用户当前目录中寻找包含的文件,若找不到,再按照标准方式寻找。所以调用库函数时一般用<文件名>方式来包含,可以节省寻找时间。如果是用户自己编写的文件,一般用“文件名 ”形式。


示意图表示在编译预处理时,要对#include命令进行文件包含处理,即把file2.c文件的全部内容复制插入到#include“file2.c”命令处,处理后得到的结果如第三个图。在编译中将包含以后的file1.c作为一个源文件单位进行编译。文件包含命令作用很大,在编程时可以节省很多时间,具体就不进行举例。在编程中慢慢体会。

文件包含跟文件链接不同,文件包含只是把该包含的文件插入到包含指令位置,连成一起然后进行编译。文件包含是编译之前进行的,而文件链接是编译之后进行。这种常用在文件头部的被包含的文件称为“标题文件”或者“头部文件”,常以“.h”为后缀,如“stdio.h”。

 

文件包含命令细节说明:

1、  一个#include命令只能指定一个被包含文件,即一对一包含。

2、  在一个被包含文件中又可以包含另一个被包含的文件,即文件包含是可以嵌套的。

3、  被包含文件(file2.h)与其所在的文件(file1.h),在预编译后已成为同一个文件而不是两个文件。所以如果file2.h中有全局静态变量,它也在file1.c文件中有效,不必用extern声明。

需要注意一点,如果一个被包含文件中有地方修改后,凡是包含此文件的所有文件都要全部重新编译。这点大家可以自己编写程序验证,文件编译时通过看编译信息可以知道。

 

条件编译

编写程序时如果对其中一些代码只在满足一定条件时才进行编译,这就是“条件编译”。

条件编译有一下几种形式:

第一种:

#ifdef  标识符

   程序段1

#else

   程序段2

#endif

这种形式条件编译的意思是,如果ifdef后面的标识符已经被#define命令定义过,,则在编译器编译程序段1,否则编译程序段2。其实#else部分可以没有,即

#ifdef  标识符

   程序段

#endif

这种条件编译的意思是,如果ifdef后面的标识符已经被#define命令定义过,,则在编译器编译程序段,否则程序段不加以编译。

程序段可以是语句组也可以是命令行,这中条件编译可以提高C语言的源程序的通用性。例如,一个C程序在不同位数处理器的计算机中运行(一个16位存放一个整数,一个是32位存放整数),这样往往需要对源程序作必要的修改,这样C语言程序的通用性比较低。但是我们可以使用条件编译来解决这个问题。例如;

#ifdef  COMPUTER

#defineINTEGER_SIZE  16

#else

#defineINTEGER_SIZE  32

#endif

这样如果是16位处理器,则宏定COMPUTER即可,编译器编译#define INTEGER_SIZE  16

否则处理器为32位,编译器编译#define INTEGER_SIZE  32  。

还有这种条件编译比较灵活,不用加注释或者删除代码就可以随便进行选择代码参与编译。

第二种:

#ifndef  标识符

   程序段

#endif

这种条件编译和第一种刚好相反,即若标识符未被定义过,则编译程序段,否则不编译。这种条件编译在处理头文件时经常用到,即文件被多个文件包含时使用这种形式可以避免重复编译。具体不举例,自己编程体验。

第三种:

#if  表达式

  程序段1

#else

  程序段2

#endif

这种条件编译的意思是:当表达式的值为真(非零)时编译程序段1,否则编译程序段2。可以事先给定一个条件,使程序在不同的条件下执行不同的功能。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值