既使用过 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++ 一种语言的源文件时,只需建立一个预编译头文件。
/* $FreeBSD$ */ #ifndef _INC_H_ #define _INC_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #endif /* ! _INC_H_ */
# $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 "___FCKpd___1lt;": # BSD Pmake: the implied source # GNU make: the first prerequisite .c.o: $(CC) $(CFLAGS) -c ___FCKpd___1lt; .cxx.o: $(CXX) $(CXXFLAGS) -c ___FCKpd___1lt; all: $(EXE) # ___FCKpd___1gt; $^ # BSD Pmake all sources not defined # GNU make not defined all prerequisites # Both interpret "$@" as target $(EXE): $(OBJS) $(LIBBDD) $(LD) $(LDFLAGS) -o $@ ___FCKpd___1gt; $^ $(LIBS) # Pre-compiled header $(OBJS): $(PCH) $(PCH): $(PCH_H) $(CC) $(CFLAGS) ___FCKpd___1gt; $^ clean: $(RM) $(PCH) $(OBJS) # For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin) $(RM) $(EXE) $(EXE).exe
如果项目既包含 C 语言源文件,也包含 C++ 语言源文件,就需要为两种语言分别维护一个预编译头文件。
/* $FreeBSD$ */ #ifndef _INC_HPP_ #define _INC_HPP_ #include "inc.h" #endif /* ! _INC_HPP_ */
# $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 "___FCKpd___3lt;": # BSD Pmake: the implied source # GNU make: the first prerequisite .c.o: $(CC) $(CFLAGS) -c ___FCKpd___3lt; .cxx.o: $(CXX) $(CXXFLAGS) -c ___FCKpd___3lt; all: $(EXE) # ___FCKpd___3gt; $^ # BSD Pmake all sources not defined # GNU make not defined all prerequisites # Both interpret "$@" as target $(EXE): $(OBJS) $(LIBBDD) $(LD) $(LDFLAGS) -o $@ ___FCKpd___3gt; $^ $(LIBS) # Pre-compiled header $(OBJS): $(PCH) $(PCH): $(PCH_H) $(CC) $(CFLAGS) ___FCKpd___3gt; $^ $(PCH_X): $(PCH_X_H) $(CXX) $(CXXFLAGS) -x c++-header ___FCKpd___3gt; $^ clean: $(RM) $(PCH) $(PCH_X) $(OBJS) # For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin) $(RM) $(EXE) $(EXE).exe
这是以上两种 Makefile 的比较:
@@ -12,6 +12,8 @@ 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 @@ -48,7 +50,10 @@ $(PCH): $(PCH_H) $(CC) $(CFLAGS) ___FCKpd___4gt; $^ +$(PCH_X): $(PCH_X_H) + $(CXX) $(CXXFLAGS) -x c++-header ___FCKpd___4gt; $^ + clean: - $(RM) $(PCH) $(OBJS) + $(RM) $(PCH) $(PCH_X) $(OBJS) # For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin) $(RM) $(EXE) $(EXE).exe