预处理命令

例题:以下关于编译预处理的叙述中错误的是( C )
A.预处理命令行必须以#开始
B.一条有效的预处理命令必须单独占据一行
C.预处理命令行只能位于源程序中所有语句之前
D.预处理命令不是C语言本身的组成部分

例题:C语言的编译系统对宏命令的处理是( D )
A.在程序运行时进行的
B.在程序连接时进行的
C.和C程序中的其他语句同时进行编译的
D.在对源程序中其他成分正式编译之前进行的

(一)预处理命令概述
凡是以“#”开头的均为预处理命令。所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
预处理命令不是C语言本身的组成部分,不能直接对它们进行编译(因为编译程序不能识别它们)。必须在对程序进行通常的编译(包括词法和语法分析、代码生成、优化等)之前,先对程序中这些特殊的命令进行“预处理”,即根据预处理命令对程序作相应的处理(例如,若程序中用#define命令定义的一个符号常量A,则在预处理时将程序中所有的A都置换为指定的字符串。若程序中用#include命令包含一个文件"stdio.h",则在预处理时将stdio.h文件中的实际内容代替该命令)。经过预处理后的程序不再包括预处理命令了,最后再由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。现在使用的许多C编译系统都包括了预处理、编译和连接等部分,在进行编译时一气呵成。因此不少用户误认为预处理命令是C语言的一部分,甚至以为它们是C语句,这是不对的。
C语言提供了多种预处理功能,如文件包含、宏定义、条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。
注意:预处理指令不是C语句,因此末尾没有分号。

(二)文件包含
C语言提供了#include文件包含预处理命令,将一个头文件包含到源程序文件中。
文件名可以带路径。
如果使用尖括号,则到系统指定包含目录去查找被包含文件。如果使用双引号,则首先在系统当前目录下查找被包含文件,没找到再到系统指定包含目录去查找。
一般使用尖括号包含系统定义的头文件,使用双引号包含用户自定义的头文件或源程序文件。

(三)宏定义
在C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
在C语言中,“宏”分为有参数和无参数两种。

(1)无参宏定义
一般形式为:#define 标识符 字符串

  • 宏定义允许嵌套
  • 习惯上宏名用大写字母表示,以便于与变量区别
  • 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。
    例如:
    #define PI 3.14159
    main(){

    }
    #undef PI
    f1(){

    }
    表示PI只在main函数中有效,在f1中无效。

(2)带参宏定义
宏定义的一般形式为:#define 宏名(形参表) 字符串
带参宏调用的一般形式为:宏名(实参表);
C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
例如:
#define M(y) yy+3y /宏定义/

K = M(5); /宏调用/

  • 宏代换只作符号代换而不作其它处理。宏定义不仅应在参数两侧加括号,也应在整个字符串外加括号。
  • 带参的宏和带参函数很相似,但有本质上的不同,把同一表达式用函数处理与用宏处理两者的结果可能是不同的。

(四)条件编译
预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调用是很有用的。
条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
条件编译有三种形式:
1)第一种形式

#ifdef 标识符
程序段1
#else
程序段2
#endif

它的功能是,如果标识符已被#define命令定义过则对程序段进行编译;否则对程序段2进行编译。
如果没有程序段2(它为空),本格式中的#else可以没有,即可以写成:

#ifdef 标识符
程序段
#endif

在程序的第一行宏定义中,定义NUM表示字符串OK

#define NUM OK

其实也可以为任何字符串,甚至不给出任何字符串,写为:

#define NUM

也具有同样的意义。
2)第二种形式

#ifndef 标识符
程序段1
#else
程序段2
#endif

与第一重形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反
3)第三种形式

#if 常量表达式
程序段1
#else
程序段2
#endif

它的功能是,如果常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。


版权声明:本文为m0_46786069原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。