GCC中使用预编译头文件

既使用过 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++ 一种语言的源文件时,只需建立一个预编译头文件。

  1. 建立一个头文件,例如命名为 inc.h。该文件供项目中所有的 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_ *
  2. 建立 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++ 语言源文件,就需要为两种语言分别维护一个预编译头文件。

  1. 再建立一个头文件,例如命名为 inc.hpp。inc.h 供 C 语言源文件使用,而 inc.hpp 供 C++ 语言文件使用。假如 inc.hpp 的内容与 inc.h 的相同,只需要简单的写上:
    /* $FreeBSD$ */
    #ifndef _INC_HPP_
    #define _INC_HPP_

    #include "inc.h"

    #endif /* ! _INC_HPP_ */
  2. 在 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 的比较:

@@ -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) $> $^

+$(PCH_X): $(PCH_X_H)
+ $(CXX) $(CXXFLAGS) -x c++-header $> $^
+
clean:
- $(RM) $(PCH) $(OBJS)
+ $(RM) $(PCH) $(PCH_X) $(OBJS)
# For Both UNIX-like OS and Microsoft Windows (MinGW/Cygwin)
$(RM) $(EXE) $(EXE).exe

转自 http://zj880725.blog.163.com/blog/static/86190120200951781657881/




=========================
简单尝试

公司开发了一个自己的基础类库,有着庞大的头文件。为了便于使用并提高编译速度,尝试了一下GCC的预编译头文件的功能:

1、先定义头文件afl.h,内容如下:(afl意味a framework library)


#ifndef _AFL_H_
#define  _AFL_H_

// from here to include all head files
#include  < Log.h >
#include 
< DebugUtil.h >

#endif

2、预编译这个头文件:
g++ -o afl.h.gch -x c++-header -c afl.h -g -Wall -I"libpath..."
   使用-x c++-header来说明这个文件作为C++的预编译头文件。注意:C和C++的处理方式不一样,C要使用-x c-header选项。
3、经过上一步,alf.h.gch的这个预编译的头文件已经产生了。下面是代码中的引用方式:

// test.cpp
#include  " alf.h "

int  main()
{
    LOG(
" test " );
    
return   1 ;
}

    注意:所有的需要库的地方,都引用alf.h就可以了。
4、编译这个文件:
g++ -o test.o -c test.cpp -g -Wall
  呵呵,发现编译的时候相当快,而且不用指定库的头文件路径。

 

转自 http://blog.youkuaiyun.com/ah__fu/archive/2008/04/08/2260330.aspx


                
<think>嗯,用户让我解释一下C++的预编译头文件。好的,首先我需要回忆一下自己对预编译头文件的理解,然后组织成一个清晰的解释。预编译头文件,通常简称为PCH,应该是在C++编译过程中用来加快编译速度的一种技术吧。那具体是怎么工作的呢? 我记得,当编译一个项目时,特别是在大型项目中,很多头文件会被反复包含,比如标准库头文件或者第三方库的头文件。每次编译源文件时,这些头文件都需要被解析和处理,这会消耗很多时间。预编译头文件的作用应该是把这些常用的、不经常变动的头文件预先编译成一种中间形式,这样在后续的编译过程中,可以直接加载这个预编译好的版本,而不需要每次都重新解析,从而节省时间。 那预编译头文件具体是怎么生成的呢?可能是在编译过程中,编译器将这些头文件处理成一个二进制文件,比如在GCC中是.gch文件,在Visual Studio中是.pch文件。然后,当其他源文件需要包含这些头文件时,编译器会检查是否有对应的预编译头文件存在,如果有的话就直接使用,而不是从头开始处理。 不过,使用预编译头文件的时候需要注意什么?比如,预编译头文件的内容应该是相对稳定的,如果里面的内容经常变动,反而可能增加编译时间,因为每次都需要重新生成。另外,不同的编译器可能有不同的实现方式,所以在跨平台开发时需要特别注意配置方法。 还有,用户可能想知道如何实际使用预编译头文件。比如,在项目中如何设置,常见的做法是把常用的头文件比如<iostream>、<vector>等放在一个预编译头文件中,比如stdafx.h或者pch.h,然后在编译时指定这个头文件进行预编译。不同的IDE和构建系统可能有不同的配置方式,比如在Visual Studio中可以通过项目属性设置,而在CMake中也有相应的命令来启用预编译头文件。 另外,可能需要提到一些使用预编译头文件的最佳实践,比如避免在预编译头中包含经常变动的头文件,或者确保预编译头文件的顺序和依赖关系正确,以免出现编译错误。还有,预编译头文件通常只对紧随其后的include有效,所以需要把预编译头文件放在源文件的最前面。 可能用户是刚开始学习C++,或者遇到了编译时间过长的问题,想了解优化方法。所以需要解释清楚预编译头文件的作用、原理和使用方法,同时指出其优缺点,让用户能够判断是否适合在他们的项目中使用。 另外,可能需要注意术语的准确性,比如预编译头文件和普通头文件的区别,预处理和编译阶段的区别。确保用户不会混淆这些概念。同时,举一些例子会有助于理解,比如在项目中如何实际应用,或者对比使用前后的编译时间差异。 最后,总结一下预编译头文件的适用场景和注意事项,帮助用户全面理解这一技术。</think>C++ 的**预编译头文件(Precompiled Header, PCH)** 是一种优化编译速度的技术,主要目的是减少大型项目中重复编译相同头文件的时间。以下是其核心要点: --- ### **工作原理** 1. **预处理与编译分离** 头文件(如 `#include <vector>`)通常包含大量模板、宏和声明。每次编译源文件时,这些内容会被重复解析。 **预编译头文件** 会将这些高频使用头文件预先编译成一种中间格式(二进制文件),后续编译时直接复用,跳过重复的解析和语法分析。 2. **文件格式** - **GCC/Clang**:生成 `.gch` 文件(如 `stdafx.h.gch`)。 - **MSVC (Visual Studio)**:生成 `.pch` 文件(如 `pch.pch`)。 --- ### **使用场景** - **大型项目**:依赖大量标准库(如 STL)、第三方库(如 Boost)或项目内通用头文件时,效果显著。 - **频繁编译**:适合开发阶段需反复编译的场景,如调试、迭代。 --- ### **配置方法(示例)** 1. **创建预编译头文件** 通常命名为 `pch.h` 或 `stdafx.h`,包含高频头文件: ```cpp // pch.h #include <iostream> #include <vector> #include "CommonProjectHeaders.h" ``` 2. **编译器配置** - **GCC/Clang**: ```bash g++ -xc++-header pch.h -o pch.h.gch ``` - **MSVC**:在项目属性中启用 `/Yu`(使用)和 `/Yc`(生成)选项。 - **CMake**:使用 `target_precompile_headers` 命令。 3. **源文件使用** 在源文件开头显式包含预编译头文件: ```cpp // main.cpp #include "pch.h" // 必须位于首位 // 其他代码... ``` --- ### **优点** - **显著减少编译时间**:尤其对重复包含的大型头文件效果明显。 - **减少内存占用**:避免重复解析同一头文件。 --- ### **注意事项** 1. **内容稳定性** 预编译头文件内容应尽量稳定,频繁修改会导致重新生成,抵消优化效果。 2. **平台兼容性** 不同编译器实现不同(如 GCC 的 `.gch` 必须与源文件同名),跨平台项目需注意配置差异。 3. **依赖顺序** 预编译头必须**在源文件的最开始包含**,否则可能失效或引发错误。 --- ### **替代方案** - **模块(C++20)**:更现代的解决方案,通过 `import` 直接引入编译后的模块,从根本上避免重复解析头文件。 --- 通过合理使用预编译头文件,可以在不修改代码的前提下,显著提升 C++ 项目的编译效率,尤其适合长期维护的中大型项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值