C语言-编译预处理_笔记

UTF8nsung

C语言-编译预处理

April 16,2012

1
C对源程序进行正常编译之前,先对一些符殊的预处理命令作解释,产生一个新的源程序(编译预处理) ,之后再进行通常的编译。编译预处理主要为程序调试、移植等提供便利,是非常实用的功能。
预处理命令可在程序的任何位置,但习惯上尽可能地写在源程序的开头,其作用范围从其出现的位置到文件尾。
C的预处理命令主要有:宏定义、文件包含和条件编译。

1  宏定义

宏定义分为带参数的宏定义和不带参数的宏定义。

1.1  不带参数的宏定义

不带参数的宏定义的一般形式为
#define PR printf
它的作用是在编译预处理时,将源程序中所有标识符替换成字符串。宏定义常用来定义符号常量。
使用宏定义可以筒化一些标识符书写,也可以部分地弥补 C没有元素个数可变的数组的不足,当需要修改元素个数时修改宏定义即可,无需修改程序中所有出现元素个数的地方。所以宏定义不仅提高了程序的可读性、便于调试,而且也方便了程序的移植。
无叁数的宏定义使用时要注意以下几个问题:
  1. 宏名一般用大写字母 、以便于与变量名的区别。
  2. 在编译预处理中宏名与字符串进行替换时,不作语法检查,只是简单的字符替换。只有在编译时才对己经展开宏名的源程序迸行语法检查。
  3. 宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令,这样可以灵活控制宏定义的范围。如:
    #undef OUT
  4. 宏定义时可以引用已经定义的宏名。
  5. 对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作。

1.2  带参数的宏定义

为进一步扩大宏的应用范围,在定义宏时,还可以带参数。带参数的宏定义的一般形式为
#define 标识符(参数表) 字符串
它的作用是在编译预处理时将源程序中所有标识符替换成字符串,并将字符串中的参数用实际使用的参数替换。 例如:
#define S(a,b,c) (a+b+c)/2
带参数的宏定义使用时,要注意以下几个问题:
  1. 在宏定义时,宏名和参数之间不能有空格,否则空格后面的所有字符序列都作为替换的字符串。例如,如果有:
    #define L (r) 2*PI*(r)
    则L为不带参数的宏名 ,「(r) 2*PI*(r)」作为替换宏名的字符串。
  2. 带参数的宏定义展开时只作简单的字符和参数的替换,不进行任何计算操作。所以一般在定义宏时在字符串的形式参数外面加一个小括号。如果省略,则在某些场合会得出错误的结论。 例如上例的宏定义如果改为:
    #define L(r) 2*PI*r
    如果源程序有「L(2+3)」,则编译预处理后变为「2*3.14159*2+3」,这样,值不是 L(5)的值了。
    带参数的宏名,虽然在源程序中出现的形式与函数很像,但是二者却有本质的区别。带参数的宏不涉及函数调用的流程控制转移、存储空间的分配、参数类型匹配 、参数传递及返回值问题,并且函数调用在程序运行时执行,而宏替换只在编译顶处理阶段进行。所以使用带参数的宏比用函数有更高的执行效率。

2  文件包含

「文件包含」实际上就是我们前面已经反复用到的 #include命令实现的功能, 即一个源程序文件可以包含另外一个源程序文件的全部内容。
「文件包含」不仅可以包含头文件,还可以包含用户自己写的源程序文件。
文件包含预处理命令的一般形式:{
#include <文件名>
#include “文件名"
或者
上述两种方式的区别是:第一种形式的文件名用尖括弧括起来,系统将到包含 C 库函数的头文件所在的目录中寻找文件。 第二种形式的文件名用双引号括起来,系统先在当前目录下寻找,若找不到,再到操作系统的 path 命令设置的路径中查找,最后才到 C 库函数的头文件所在目录中查找。 所以为了节省查找时间,要选择合适的形式。
「文件包含」使用时要注意:
  1. 一个#include命令只能指定一个被包含的文件。
  2. 「文件包含」可以嵌套。在文件包含的嵌套时,如果「文件1]包含了「文件2」,而「文件2」包含了「文件3」, 则在「文件1]也要包含「文件3」,并且「文件3」的包含要写在「文件2」的包含之前, 即「文件1」中的「文件包含」说明如下:
    #include ”文件 3"
    #include ”文件 2"
「文件包含」命令为多个源程序文件的组装提供了一种方法。在编写程序时,习惯上将公共的符号常量定义、数据类型定义和 exern类型的全局变量说明构成一个源文件, 并以 “.H"为文件名的后缀。如果其他文件用到这些说明时,只要包含该文件即可、无需说明,减少了工作量。而且这样编程使得各源程序文件中的数据结构、符号常量以及全局变量形式统一,便于程序的修改和调试。

3  条件编译

「条件编译」命令允许对程序中的内容选择性地编译,即可以根据一定的条件选择是否编译。
条件编译的命令主要有以下几种形式:

形式一

#ifdef `标识符``程序段1`#else`程序段2`#endif
它的作用是当「标识符」已经由 #define定义过了,则编译「程序段1」,否则编译「程序段2」。其中如果不需要编译「程序段2」,则上述形式可以变换为:#ifdef `标识符``程序段1`#endif

形式二

#ifndef `标识符``程序段1`#else`程序段2`#endif
它的作用是当「标识符」没有由 define定义过,则编译「程序段1」,否则编译「程序段2」。可见形式二与形式一的作用相反。同样当无「程序段2」时,则上述形式变换为:#ifndef `标识符``程序段1`#endif

形式三

#ifdef `表达式``程序段1`#else`程序段2`#endif
它的作用是当「表达式」值为真时,编译程序段1,否则编译程序段2。同样当元程序段2时,则上述形式变换为:#ifdef `表达式``程序段1`#endif
以上三种形式的条件编译预处理结构都可以嵌套使用。当 #e1se后嵌套 #if时, 可以使用预处理命令 #elif,它相当于 #else #if
在程序中使用条件编译主要是为了方便程序的调试和移植。

Footnotes:

1文本整理:virhuiai


File translated fromTEXby TTH,version 4.03.
On 15 May 2012, 19:01.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值