预处理器

第一,编译器首先把源代码中出现的字符映射到源字符集。

第二,编译器查找反斜线后紧跟换行符的实例并删除这些实例。

第三,编译器将文本划分成预处理的语言符号序列和空白字符及注释序列(术语语言符号代表由空格分隔的组。本章后面部分将详细讨论语言符号)。应注意的一点是编译器用一个空格字符代替每一个注释。

 

C实现可能还会选用单个空格代替每一个空白字符(不包括换行符)序列。最后,程序进入预处理阶段。预处理器寻找可能存在的预处理指令。这些指令由一行开始处的#符号标识。

 

预处理指令从#开始,到最后第一个换行符为止。也就是说,指令的长度限于一行代码。但是正如前文提到的,在预处理开始前,系统会删除反斜线和换行符的组合。因此可以把指令扩张到几个物理行,由这些物理行组成单个逻辑航。

 每个#define行(即逻辑行)由三部分组成。第一部分为指令#define自身(预处理指令)。第二部分为所选择的缩略语,这些缩略语称为宏(macro),有类对象宏(object-like macro)和类函数宏。宏的名字中不允许有空格,而且必须遵循C变量命名规则:只能使用字母、数字和下划线(_),第一个字符不能为数字。第三部分称为替换列表(replacement list)和主体。预处理器在程序中发现了宏的实例后,总会用实体代替该宏(有一种例外,以后进行说明)。从宏变成最终的替换文本的过程称为宏展开。注意,可以使用标准的C注释方法在#define行中进行注释。正如前面提到的,在预处理器处理之前,每个注释都会被一个空格所代替。

 

 1 #include <stdio.h>
 2 
 3 #define CON(x) "hello" # x
 4 #define CON2(x, y) x # y
 5 
 6 #define CONARG(x) hello ## x
 7 #define CONARG2(x, y) x ## y
 8 
 9 int main(void)
10 {
11 
12     // # 预处理运算符前面操作数必须为字符串
13     printf("%s\n", CON(world));
14     printf("%s\n", CON2("hello", world));
15 
16     printf("----------------------\n");
17 
18     // ## 
19     int helloworld = 123;
20     printf("helloworld = %d\n", CONARG(world));
21     printf("helloworld = %d\n", CONARG2(hello, world));
22 }
[root@playstation basic]# ./contact
helloworld
helloworld
----------------------
helloworld = 123
helloworld = 123
[root@playstation basic]# 

 

 1 #include <stdio.h>
 2 
 3 #define PR(...) printf(__VA_ARGS__)
 4 
 5 int main(void)
 6 {
 7     int id = 20092192;
 8     char name[] = "helloworld";
 9     float height = 176.8;
10 
11     PR("id = %d\n", id);
12     PR("id = %d, name = %s\n", id, name);
13     PR("id = %d, name = %s, height = %f\n", id, name, height);
14 }

 

 1 #include<stdio.h>
 2 
 3 int main(void)
 4 {
 5     int SIZE = 3;
 6 
 7 #define SIZE 1024
 8 
 9     printf("%d\n", SIZE);
10 
11 }

如果标识符不是宏,而是(例如)一个具有文件作用域的C变量,那么预处理器把标识符当做未定义的。

 

许多新的实现提供另一种方法来判断一个名字是否已经定义。不需使用:

    #ifdef VAX

而是采用下面的形式:

    #if defined(VAX)

这里,defined是一个预处理器运算符。如果defined的参数已用#define定义过,那么defined返回1;否则返回0。这种新方法的优点在于它可以和#elif一起使用。

#if defined(IBMPC)

  #include "ibmpc.h"

#elif defined(VAX)

  #include "vax.h"

#elif defined(MAC)

  #include "mac.h"

#endif

 

 C99标准提供一个名为__func__的预定义标识符。__func__展开为一个代表函数名(该函数包含该标识符)的字符串。该标识符具有函数作用域,而宏本质上具有文件作用域。因而__func__是C语言的预定义标识符,而非预定义宏。

 

#line指令用于重置由__LINE__和__FILE__宏报告的行号和文件名。可以这样使用#line:

#line 1000        // 把当前行号重置为1000

#line 10     "cool.c"       // 把行号重置为10, 文件名重置为cool.c

#error指令使预编译器发出一条错误消息,该消息包含指令中的文本。可能的话,编译过程应该中断。

 

在现代的编译器中,可用命令行参数或IDE菜单修改编译器的某些设置。也可用#pragma将编译器指令置于源代码中。

例如,在开发C99时,用C9X代表C99。编译器可以使用下面的编译指示(prama)来启用对C9X的支持:

  #pragma c9x on

 编译器在优化内联函数时,必须知道函数定义的内容。这意味着内联函数的定义和对该函数的调用必须在同一文件中。正因为这样,内联函数通常具有内部链接。因此,在多文件程序中,每个调用内联函数的文件都要对函数进行定义。达到这个目标的最简单方法为:在头文件中定义内联函数,并在使用该函数的文件中包含该头文件。一般不在头文件中放置可执行代码,但内联函数是个例外。因为内联函数具有内部链接,所以在多个文件中定义同一内联函数不会产生什么问题。

 

 

posted @ 2014-09-26 09:46  挨踢淫才  阅读(616)  评论(0编辑  收藏  举报