编译程序之前,先由预处理器检查程序(因此称为预处理器)。根据程序中使用的预处理指令,预处理器用符号缩略语所代表的内容替换程序中的缩略语。
常量#define
每个#define(即逻辑行)由三步分组成。
-
第一部分为指令#define本身
-
第二部分为所选择的缩略语,这些缩略语成为宏(macro)。一些宏用来代表值,它们被成为对象宏(object-like macro)。宏的名字中必须遵循C变量命名规则。
-
第三部分(#define行的剩余部分)称为替换列表(replacement list)或主体(body)。
预处理器在程序中发现了宏的实例后,总会用该实体代替该宏。从宏变成最终的替换文本的过程成为宏展开(macro expansion)。
C编译器在编译时对所有常量表达式(只包含常量表达式)求值。预处理器不进行计算,它只是按照指令进行文字替换操作。
使用const您可以创建全局常量和局部常量/数字常量/数组常量和结构常量。宏常量可以用来指定标准数组的大小并作为const的初始化值。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
在#define中使用参数
通过使用参数,可以常见外形和作用都与函数相似的类函数(function-like macro)。宏的参数也用圆括号括起来。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
其中,SQURE为宏标识符,SQUARE(X)中的X为宏的参数,x*x为替换列表。
利用宏参数差un关键字符串:#运算符
在类函数宏的替换部分中,#符号用做一个预处理运算符,它可以把语言符号转化为字符串。
预处理器的粘合剂:##运算符
##运算符可以用于函数宏的替换部分。
#include文件包含
预处理器发现#include指令后,就会寻找后跟的文件名,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换代码文件中的#include指令,就像把被包含文件中的全部内容键入到源文件中的这个特定位置一样。
#incldue < stido.h > | 文件名放在尖括号中 |
#include “mystuff.H” | 文件名放在双引号中 |
在UNIX系统中,尖括号告诉预处理器在一个或多个标准系统中寻找文件。双引号告诉预处理器在当前目录(或文件名中指定的目录)中寻找文件,然后在标准位置寻找文件。
#incldue < stido.h > | 搜索系统目录 |
#include “mystuff.H” | 搜索当前目录 |
#include “/usr/biff/p.H” | 搜索/usr/biff目录 |
使用头文件
头文件内容的最常见形式包括:
-
明显常量 例如:典型的stdio.h文件定义EOF/NULL/BUFSIZE(标准I/O缓冲区的大小)
-
宏函数 例如:getchar()通常被定义为getc(stdin),getc()通常定义为较复杂的宏,而头文件ctype.h通常包含ctype函数的定义。
-
函数声明 例如:头文件string.h包含字符串函数系列的函数声明。
-
结构模板定义 标准I/O函数使用FILE结构,该结构包含文件及文件相关缓冲区的信息。头文件stdio.h中存放FILE结构的声明。
-
类型定义
可以使用头文件来声明多个文件共享的外部变量。此时,可以在包含函数声明的源代码文件定义一个具有文件作用域/外部链接的变量。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
该行代码会出现在包含了该头文件的任何文件中,并使得使用这个头文件的文件都可以使用该变量。
其他指令
预处理器提供一些指令来帮助程序员编写这样的代码:
- 改变一些#define宏的值后,这些代码可以从一个系统移植到另一个系统。
- #undef指令取消前面的#define定义
- #if/#ifdef/#ifndef/#else/#elif/#endif指令可以用于选择声明情况下编译那些代码。
- #line指令用于重置行和文件信息
- #error指令用于给出错误消息
- #pragma指令用于向编译器发出指令
#undef指令
#undef指令用于取消定义一个给定的#define
- 1
- 2
- 1
- 2
取消定义后可以重新定义LIMIT,以使他有一个新的值。即使开始没有定LIMIT,取消LIMIT定义也是合法的。
如果想使用一个特定名字,但又不能确定前面是否已经使用了该名字,为了安全起见,就可以取消该名字的定义。
宏定义注意点
#define宏的作用域从文件中的定义点开始,直到用#undef指令取消宏为止,或直到文件尾为止(由二者中最先满足的那个结束宏的作用域)。还应该主语,如果用头文件引入宏,那么,#define在文件中的位置依赖于#include指令的位置。
条件编译
可以使用指令告诉编译器:根据编译时的条件接受或忽略代码块。
#ifdef #else 和 #endif指令
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
#ifef #else格式非常类似于C中的if else。主要差异为预处理器不能够识别标记代码块的花括号{},因此使用#else(如果需要)和#endif(必须存在)来标记指令块。
可以使用这种方法辅助调试程序。
#ifndef指令
#ifnedf指令可以与#else/#endif指令一起使用。#ifndef判断后面的标识符是否为未定义的。
一般的,当某个文件包含几个头文件,而且每个头文件都可能包含相同的宏时,使用#ifndef可以防止对该宏重复定义。此时,第一个头文件中的定义变成有效定义,而其他头文件的定义则被忽略。
#ifndef指令通常用于防止多次包含同一个文件,头文件可以采用类似下面的设置:
- 1
- 2
- 3
- 1
- 2
- 3
为什么会多次包含同一个文件呢?最常见的原因是:许多包含文件自身包含了其他文件,因此可能显示的包含其他文件已经包含的文件。
为什么这个会成为问题? 因为头文件中的有些语句在一个文件中只能出现一次。
所以,通常采用#ifndef来解决这个问题。
#if和#elif指令
#if 指令更像常规的C中的if:#if后跟常量整数表达式。如果表达式非零为真,零为假。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
defined()是一个预处理器运算符。如果defined的参数已用#define定义过,则defined返回1。它的有点在于可以和#elif一起使用.