预处理器的工作方式

来源:互联网 发布:cad2007软件自学网 编辑:程序博客网 时间:2024/05/24 04:06

       在程序里我们始终有话要说,但是所有已知的语言都无法表述得很好。

在前章中我们用过#define与#include指令,但没有深入讨论它们。这些指令,以及我们还没有学到的指令,都是由预处理器处理的。预处理器是一个小软件,它可以在编译前编辑C程序。C语言(和C++语言)因为依赖预处理器而不同于其他的编程语言。

预处理器是一个强大的工具,但它同时也可能是许多难以发现的错误的根源。同时,预处理器也经常被错误地用来编写出一些几乎不可能读懂的程序。尽管有些C程序员十分依赖于预处理器,我依然建议适度地使用它,就像许多其他生活中的事物一样。现代的C语言编程风格呼吁减少对于处理器的依赖。在C++中,对语言的改变使得可以更进一步限制预处理器的使用。

本章首先会描述预处理器的工作过程(14.1节),并且给出一些会影响全部预处理指令的通用规则(14.2节)。14.3节和14.4节涵盖介绍预处理器最主要的两种能力:宏定义和条件编译。(而处理器另外一个主要功能(即文件包含)将留到第15章再进行详细介绍。)14.5节讨论较少用到的预处理指令:#error、#line和#pragma。

14.1  预处理器的工作方式

预处理器的行为是由指令控制的。这些指令是由#字符开头的一些命令。我们已经在前几章中遇见过其中两种指令,#define和#include。

#define指令定义了一个宏——用来代表其他东西的一个名字,通常是某一类型的常量。预处理器会通过将宏的名字和它的定义存储在一起来响应#define指令。当这个宏在后面的程序中使用到时,预处理器“扩展”了宏,将宏替换为它所定义的值。

#include指令告诉预处理器打开一个特定的文件,将它的内容作为正在编译的文件的一部分“包含”进来。例如,下面这行指令

#include <stdio.h>

文本框:  指示预处理器打开一个名字为stdio.h的文件,并将它的内容加到当前的程序中。(stdio.h包含了C语言标准输入/输出函数的原型。)

右图说明了预处理器在编译过程中的作用。预处理器的输入是一个C语言程序,程序可能包含指令。预处理器会执行这些指令,并在处理过程中删除这些指令。预处理器的输出是另一个程序:原程序的一个编辑后的版本,不再包含指令。预处理器的输出被直接交给编译器,编译器检查程序是否有错误,并经程序翻译为目标代码(机器指令)。

为了展现预处理器的作用,我们将它应用于2.6节的程序celsius.c。下面是原来的程序:

/* Converts a Fahrenheit temperature to Celsius */

#include <stdio. h>

#define FREEZING_PT 32.0

#define SCALE_FACTOR (5.0 / 9.0)

main()

{

  float fahrenheit, celsius;

  printf("Enter Fahrenheit temperature: ");

  scanf("%f", &fahrenheit);

  Celsius = (fahrenheit - FREEZING_PT)  * SCALE_FACTOR;

  printf("Celsius equivalent is: %.if/n", celsius);

   return 0;

}

预处理结束后,程序是下面的样子:

空行

空行

从stdio.h中引入的行

空行

空行

空行

空行

main()

{

  float fahrenheit, celsius;

  printf("Enter Fahrenheit temperature:  ");

  scanf("%f", &fahrenheit);

  celsius = (fahrenheit - 32.0)  *  (5.0 / 9.0);

  printf("Celsius equivalent is: %.if/n", celsius);

  return 0;

}

预处理器通过把stdio.h的内容加入来响应#include指令。由于长度的原因,这里没有将stdio.h的内容显示出来。预处理器也删除了#define指令,并且替换了该文件中稍后出现在任何位置上的FREEZING_PT和SCALE_FACTOR。请注意预处理器并没有删除包含指令的行,而是简单地将它们替换为空。

正如这个例子所展示的那样,预处理器不仅仅是执行了指令,还做了一些其他的事情。特别值得注意的是,它将每一处注释都替换为一个空格字符。有一些预处理器还会进一步删除不必要的空白字符,包括在每一行开始用于缩进的空格符和制表符。

在C语言较早的时期,预处理器是一个单独的程序,并将它的输出提供给编译器。如今,预处理器通常和编译器集成在一起(为了提高编译的速度)。然而,我们仍然将它们认为是不同的程序。实际上,大部分C编译器提供一些方法,使用户可以看到预处理器的输出。一些编译器在打开特定的选项时(UNIX环境下通常是-P)仅产生预处理器的输出。其他一些编译器会提供一个独立的程序,这个程序的工作与集成的预处理器一致。如果需要更多的信息,可以查看你使用的编译器的文档。

注意,预处理器仅知道少量C语言的规则。因此,它在执行指令时非常有可能产生非法的程序。经常是源程序看起来没问题,使错误难以查找。对于较复杂的程序,检查预处理器的输出可能是找到这类错误的有效途径。