C/C++的程序通常有两个部分,一个是.h头文件,是通常.c实现文件头上引入的外部引入(include)的程序接口。自从David Parnas提出信息掩蔽原则后【2】,写程序要注意把接口和实现分离开来。虽然这篇文章的历史很悠久,想研究软件工程的朋友还是一定要读一读。
这就带来了一个小问题:接口,或者我们说的应用程序接口API,往往声明了大于实现需要的内容。比如说,
#include <stdio.h>
int main(int argc, char ** argv) {
printf(“Hello, world!\n”);
}
以上程序是最小的例子了。可是,你要是把头文件stdio.h全部展开,会引入946行代码!946行代码都长什么样呢?大概是这个样子:
。。。
23 #ifndef _STDIO_H
24
25 #if !defined __need_FILE && !defined __need___FILE
26 # define _STDIO_H 1
27 # include <features.h>
28
29 __BEGIN_DECLS
30
31 # define __need_size_t
32 # define __need_NULL
33 # include <stddef.h>
34
35 # include <bits/types.h>
36 # define __need_FILE
37 # define __need___FILE
38 #endif /* Don’t need FILE. */
。。。
比这还绝的是,引入的第27行又递归地引入更多的头文件,又是163行代码。。。如此一来一去(术语称为预处理),你会得到852行代码,让编译器去理解。
可是,从这个程序的意图上说,你只需要那么一行声明:
extern int printf (const char *__restrict __format, …); 就能够编译通过了。
问题说清楚了,当你的程序引入头文件的时候,编译器不分青红皂白的处理全部信息,就会浪费宝贵的编译时间。如果有办法自动地规整程序,简化为只引入必要的信息,就能够有效地缩减编译器计算的时间。
Java和其他程序语言当然也有类似的问题,但是在C/C++程序中表现得尤其明显。因为系统软件,比如Linux Kernel, DB2,嵌入式系统,等等往往很复杂,需要定义许许多多头文件,把它们整理清楚是很有用的工具。