既使用过 Microsoft® Visual C++® 又使用过 GNU CC 的网友一定会感受到两者编译速度的差异,尤其是对于 wxWidgets 这样头文件内容多的软件。Microsoft® Visual C++® 能够有很高编译效率的原因是其支持“预编译头文件” (Pre-Compiled Header, PCH)。当使用 Microsoft® Visual C++® 建立项目时,常常会建立文件 StdAfx.cpp 和 StdAfx.h。其中 StdAfx.h 包含了项目中所有实质 C/C++ 源文件所要用到的一些系统头文件,而 StdAfx.cpp 只包含了 StdAfx.h。这两个文件便是用来建立预编译头文件“项目名.pch”的。预编译头文件是将一些项目中普遍使用的头文件内容的词法分析、语法分析等结果缓存在一个特定格式的二进制文件中;当然编译实质 C/C++ 源文件时,就不必从头对这些头文件进行词法-语法分析,而只需要利用那些已经完成词法-语法分析的结果就可以了。

事实上,GNU CC 从 3.4.x 版和 4.x 版开始,也支持了这种提高编译效率的机制。只是由于 GNU CC 的手册中的《Using Precompiled Headers》一节对此介绍不多,也没有简单的自动项目管理工具支持这项功能,因而许多网友还不知道 GNU CC 的这项功能。

GNU CC 的手册中建议使用 make 管理预编译头文件,还指出 C 语言的预编译头文件和 C++ 语言的预编译头文件是不一样的。这里首先讲述项目中只有 C 语言源文件或只有 C++ 语言源文件的情形,再讲述项目中两种语言的源文件同时存在的情况。

项目中只有 C 或 C++ 一种语言的源文件时,只需建立一个预编译头文件。

  • 建立一个头文件,例如命名为 inc.h。该文件供项目中所有的 C/C++ 源文件使用。将整个项目所需要的头文件都列在其中:
 1/* $FreeBSD$ */
 2#ifndef	_INC_H_
 3#define	_INC_H_
 4
 5#include <stdio.h>
 6#include <stdlib.h>
 7#include <string.h>
 8#include <fcntl.h>
 9#include <sys/types.h>
10#include <sys/uio.h>
11#include <unistd.h>
12#include <sys/socket.h>
13#include <netinet/in.h>
14#include <arpa/inet.h>
15
16#endif /* ! _INC_H_ */```
17 
18+ 建立 Makefile,以维护预编译头文件。一方面要建立维护 GNU CC 的预编译头文件 inc.h.gch 的规则;另一方面,要在编译每个 C/C++ 源文件时检查 inc.h.gch,即让所有 .o 文件依赖于 inc.h.gch 

\(FreeBSD\)

CC = gcc CFLAGS = -g -Wall

CXX = gcc CXXFLAGS = -g -Wall

LD = gcc LDFLAGS = -g -Wall

EXE = testapp PCH_H = inc.h PCH = inc.h.gch SRCS = testapp.c OBJS = testapp.o LIBS = # System Libraries

ECHO = echo CP = cp -v RM = rm -f

.SUFFIXES: .SUFFIXES: .o .c .cxx

The meaning of “$<”:

BSD Pmake: the implied source

GNU make: the first prerequisite

.c.o: \((CC) \)(CFLAGS) -c \(< .cxx.o: \)(CXX) \((CXXFLAGS) -c \)<

all: $(EXE)

\(> \)^

BSD Pmake all sources not defined

GNU make not defined all prerequisites

Both interpret “$@” as target

\((EXE): \)(OBJS) \((LIBBDD) \)(LD) \((LDFLAGS) -o \)@ \(> \)^ $(LIBS)

Pre-compiled header

\((OBJS): \)(PCH)

\((PCH): \)(PCH_H) \((CC) \)(CFLAGS) \(> \)^

clean: \((RM) \)(PCH) $(OBJS)

For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin)

$(RM) $(EXE) $(EXE).exe```

如果项目既包含 C 语言源文件,也包含 C++ 语言源文件,就需要为两种语言分别维护一个预编译头文件。

  • 再建立一个头文件,例如命名为 inc.hpp。inc.h 供 C 语言源文件使用,而 inc.hpp 供 C++ 语言文件使用。假如 inc.hpp 的内容与 inc.h 的相同,只需要简单的写上:
1/* $FreeBSD$ */
2#ifndef	_INC_HPP_
3#define	_INC_HPP_
4
5#include "inc.h"
6
7#endif /* ! _INC_HPP_ */```
8 
9+  Makefile 里也要随之增加对 inc.hpp 的维护。一是要增加产生 inc.hpp.gch 的规则,此时执行 GNU CC 时要增加参数“-x c++-header”;二是要在 clean 一节中删除这个预编译头文件。 

\(FreeBSD\)

CC = gcc CFLAGS = -g -Wall

CXX = gcc CXXFLAGS = -g -Wall

LD = gcc LDFLAGS = -g -Wall

EXE = testapp PCH_H = inc.h PCH = inc.h.gch PCH_X_H = inc.hpp PCH_X = inc.hpp.gch SRCS = testapp.c OBJS = testapp.o LIBS = # System Libraries

ECHO = echo CP = cp -v RM = rm -f

.SUFFIXES: .SUFFIXES: .o .c .cxx

The meaning of “$<”:

BSD Pmake: the implied source

GNU make: the first prerequisite

.c.o: \((CC) \)(CFLAGS) -c \(< .cxx.o: \)(CXX) \((CXXFLAGS) -c \)<

all: $(EXE)

\(> \)^

BSD Pmake all sources not defined

GNU make not defined all prerequisites

Both interpret “$@” as target

\((EXE): \)(OBJS) \((LIBBDD) \)(LD) \((LDFLAGS) -o \)@ \(> \)^ $(LIBS)

Pre-compiled header

\((OBJS): \)(PCH)

\((PCH): \)(PCH_H) \((CC) \)(CFLAGS) \(> \)^

\((PCH_X): \)(PCH_X_H) \((CXX) \)(CXXFLAGS) -x c++-header \(> \)^

clean: \((RM) \)(PCH) \((PCH_X) \)(OBJS)

For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin)

$(RM) $(EXE) $(EXE).exe```

这是以上两种 Makefile 的比较:

 1@@ -12,6 +12,8 @@
 2 EXE    =   testapp
 3 PCH_H  =   inc.h
 4 PCH    =   inc.h.gch
 5+PCH_X_H    =   inc.hpp
 6+PCH_X  =   inc.hpp.gch
 7 SRCS   =   testapp.c
 8 OBJS   =   testapp.o
 9 LIBS   =           # System Libraries
10@@ -48,7 +50,10 @@
11 $(PCH): $(PCH_H)
12    $(CC) $(CFLAGS) $> $^
13
14+$(PCH_X): $(PCH_X_H)
15+   $(CXX) $(CXXFLAGS) -x c++-header $> $^
16+
17 clean:
18-   $(RM) $(PCH) $(OBJS)
19+   $(RM) $(PCH) $(PCH_X) $(OBJS)
20 # For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin)
21    $(RM) $(EXE) $(EXE).exe```
22
23
24  本文转自 
25[使用 GNU CC 的预编译头文件加快编译速度](http://www.codeweblog.com/%E4%BD%BF%E7%94%A8-gnu-cc-%E7%9A%84%E9%A2%84%E7%BC%96%E8%AF%91%E5%A4%B4%E6%96%87%E4%BB%B6%E5%8A%A0%E5%BF%AB%E7%BC%96%E8%AF%91%E9%80%9F%E5%BA%A6/)