关于预处理的“常识”

  • 预处理并不是编译,也不是“预编译”。

  • 预处理并不是每个语言都有的。

  • C/C++预处理仅仅是把源程序划分和整理成一个个的段(phase),并不进行编译。

  • 预处理器在UNIX传统中通常缩写为PP,在自动构建脚本中C预处理器被缩写为CPP的宏指代。为了不造成歧义,C++(c-plus-plus) 经常并不是缩写为CPP,而改成CXX。


C语言预处理符号

ANSI C标准要求支持的预处理符号包括:

#define、#undef(宏定义), #include(文件包含), #if、#else、#elif、#endif、#ifdef、#ifndef(条件编译), #line(行控制), #error(错误处理), #pragma(实现相关), #(转义),##(参数连接)。


宏定义的注意事项

试图使用宏去定义注释符号是不行的,例如以下代码:

    #define BSC //
    #define BMC /*
    #define EMC */
    BSC my single-line comment
    BMC my multi-line comment EMC


因为注释先于预处理指令处理,当展开这些宏定义的时候自然会出现一堆错误。

宏定义表达式的时候一定不能吝啬括号这个不用解释了。

宏定义的时候宏名中最好不要有空格。下面的定义会带来很多麻烦:

    #define SUM (x) ((x)*(x))

一旦使用了#undef撤销宏,则后面的代码都不能使用这个宏,除非再次定义。此外,如果没有#undef的情形下就直接再次定义,后来的定义会覆盖掉前面的定义。下面代码中的c值是4:

    #include <stdio.h>

    #define X 3
    #define Y X*2
    #undef X
    #define X 2

    int c = Y;

    int main(int argc, char** argv){
        printf("%d",c) ;
        return 0;
    }

宏仅在使用的时候展开,否则即使定义有问题,也不会编译出错。如果把上面代码中的第二个#define注释掉,并把C的值赋值为0,即撤销了X定义,也不会报错,因为没有使用Y,也就不会展开。


#pragma的常用参数

message:用于在编译窗口输出信息中显示对应信息。下面是一个典型的应用:

    #ifdef _X86
        #pragma message("x86 macro is activated.");
    #endif

code_seg:在开发驱动程序的时候会用到。

once: 指定(确保)仅编译一次该文件(一般是头文件)。

hdrstop:表示不编译后面的头文件。

pack:指定内存对齐参数。


#符号和##符号

#符号用于转义。这么说起来不好理解,看例子:

    #define PRTSQR(x) printf("The sqr of x is %d.\n", ((x)*(x)));

如果执行PRTSQR(4),则这句代码的输出是:

The sqr of x is 16.

如果希望显示参数的值,那么在字符串中的字符x前面加一个#并加一个引号,即:

    #define PRTSQR(x) printf("The sqr of "#x" is %d.\n", ((x)*(x)));

输出结果就成了:

The sqr of 4 is 16.


##运算符用于连接,例如下面的宏:

    #define XNAME(n) x##n
    int XNAME(8);

上面的第二行代码会被展开为x8。


关于预处理符号先总结到这里。