C预处理器在程序执行之前查看程序(故称为预处理器),根据程序中的预处理器指令,预处理器把符号缩写替换成其表示的内容。预处理器可以包含程序所需的其他文件,可以选择让编译器查看哪些代码。预处理并不知道C,基本上它的工作是把一些文本转换成另外一些文本。这样描述预处理器无法体现它的真正效用和价值。
#define
#define指令来定义明示常量(manifest constant,也叫符号常量),但是指令还有许多其他用途。
#define TWO 2
#define PX printf("X is %d.\n", x)
程序有以下代码
int x = TWO;
PX;
分析具体过程
x = TWO则是变成x = 2
而PX则是变成printf("X is %d.\n", x),则会打印
X is 2.
看看下面分析
#define PX printf("X is %d.\n", x)
分三部分:
- 第1部分是#define指令本身;
- 第2部分是选定的缩写,像本指令行的PX,也称为宏,这些宏被称为类对象宏(object-like macro)。
- 第3部分称为替换列表或替换休,像printf("X is %d.\n", x),一旦预处理器在程序中找到宏的示实例后,就会用替换体代替该宏。从宏变成最终替换文本的过程称为宏展开。
预处理器黏合剂: ##运算符
与#运算符类似,##运算符可用于类函数宏的替换部分,而且,##还可用于对象宏的替换部分,##运算符把两个记号组合成一个记号,比如:
#define XNAME(n) x ## n
然后,宏XNAME(4)将展开为x4。
typedef
typedef声明,简称typedef,为现有类型创建一个新的名字,比如:
struct mystruct {
int id
};
我们在定义一个结构变量时,可以这样
struct mystruct myvar;
其实可以更简单点,使用typedef关键字,
typedef struct mystruct {
int id
} yourstruct;
这样就是为结构体类型struct mystruct {...} 声称一个别名类型,并没有创建一个新类型,只是为原来的结构类型声明一种别名yourstruct,以后再想定义一个结构变量,可以这样:
yourstruct yourvar;
文件包含: #include
预处理器发现#include指令时,会查看后面的文件名,并把文件的内容包含到当前文件中,即替换源文件中的#include指令。
指令#undef
#undef指令用于“取消”已定义的#define指令
条件编译
可以使用其他指令创建条件编译(conditional compilation)。也就是说,可以使用这些指令告诉编译器根据编译时的条件执行或忽略信息(或代码)块。
1.#ifdef、#else和#endif指令
看看条件编译的例子:
#ifdef MAVIS
#include "horse.h"
#define STABLES 5
#else
#include "cow.h"
#define STABLES 1
#endif
看上面,根据常量MAVIS是否定义了,则包含不同的头文件、定义常量STABLES不同的值。
这里的例子是新的编译器和ANSI标准支持的缩进格式,旧的编译器,必须左对齐所有的指令或至少左对齐#号。
2.#ifndef指令
#ifndef指令与#ifdef指令的用法类似,也可以和#else,#endif一直使用,但是它们的逻辑相反。#ifndef指令判断后面的标识符是否未定义的,常用于定义之前未定义的常量。
#ifndef SIZE
#define SIZE 100
#endif
3.#if和#elif指令
#if指令很像C语言中的if。#if后面跟整形常量表达式,如果表达式为非零,则表达式为真,如:
#if SYS == 1
#include "limpc.h"
#elif SYS == 2
#include "vax.h"
#else
#include "general.h"
#endif
当然也可以判断是否定义了常量:
#if defined (VAX)
即使用#if defined (VAX)代替#ifdef VAX
4.#line和#error
#line指令重置LINE和FILE宏报告的行号和文件名。
#line 1000 // 把当前行号重置为1000
#line 10 "cool.c" // 把行号重置为10,把文件名重置为cool.c
#error指令让预处理器发出一条错误消息,该消息包含指令中的文本,如果可能的话,编译过程应该中断,像这样:
#if __STDC_VERSION__ != 20L
#error Not C11
#endif
5.#pragma
现在编译器中,可以通过命令行参数修改编译器的一些设置。#pragma把编译器指令放话源代码中。
例如,在开发C99时,标准被称为C9X,可以使用下面的编译指示(pragma)让编译器支持C9X:
#pragma c9x on
泛型
..