C 预处理器


C预处理器是在编译源码前对源码进行基本处理(如文本替换、头文件内容替换等)的一个程序。


预处理器扫描源码中的预编译指令并进行相应的处理。所有预处理指令都以#符号开始。

文件包含指令

文件包含指令#include有两种写法:

  • #include <a.h>
  • #include "a.h"

源码中的文件包含指令行会被文件a.h的内容替换。第1种写法从C标准库中查找a.h文件(和实现有关),第2种写法从源码所在目录查找a.h文件。

宏替换指令

宏替换指令有2种:

  • #define 宏名 替换文本,源码中所有宏名出现的地方都被替换成替换文本
  • #define add(a, b) (a + b),带参数的宏,可以用来模拟函数。

C预定义了一些宏用于调试:

  • FILE:当前文件名,作为一个字符串字面量。
  • LINE:源码的当前行数,作为一个数字字面量。
  • DATE:当前系统日期,作为一个字符串。
  • TIME:当前系统时间,作为一个字符串。

#操作符:number-sign operator,将宏的实参转换成一个字符串常量。该操作符只能用于有参数或参数列表的宏。 ##操作符:token-pasting operator,符号连接操作符,用于宏扩展时串连宏的实参。

下面是宏替换指令的例子:

#include <stdio.h>
#define SIZE 10
#define like(a, b) printf(#a " like " #b " \n")
#define token_concat(i) printf("value = %s \n", name##i)

int main(int argc, const char * argv[]) {
    printf("size : %d \n", SIZE);   // 10

    printf("file :%s \n", __FILE__ ); // macro.c
    printf("line :%d \n", __LINE__ ); // 10
    printf("date :%s \n", __DATE__ ); // Apr 30 2018
    printf("time :%s \n", __TIME__ ); // 17:12:35

    // #操作符
    like(cat, fish);  // cat like fish

    // ##操作符
    char *name2 = "apple";
    token_concat(2);  // value = apple 

    return 0;
}

条件编译指令

条件编译指令有下面几种:

  • #ifdef,如果定义了指定宏,返回true。
  • #ifndef,如果没有定义指定宏,返回true。
  • #if,判断编译时条件是否为true。
  • #else,和#if一起使用。
  • #elif,和#if一起使用。
  • #undef,取消宏的定义。

defined()操作符和#if指令一起使用用于判断是否定义了指定宏。

下面是条件编译指令的例子:

#include <stdio.h>
#define SIZE 10

#if !defined(COLOR)
#define COLOR "red"
#endif

#ifndef FRUIT
#define FRUIT "pear"
#endif

int main(int argc, const char * argv[]) {
    printf("color : %s \n", COLOR); // red
    printf("fruit : %s \n", FRUIT); // pear

    return 0;
}