浅谈GCC预编译头技术
浅谈GCC预编译头技术
文/jorge
——谨以此文,悼念我等待MinGW编译时逝去的那些时间。
其 实刚开始编程的时候,我是丝毫不重视编译速度之类的问题的,原因很简单,因为那时我用BASICA。后来一直用到C++ Builder,尽管Borland的广告无时无刻不在吹嘘其编译速度,我却从没有对这个问题上心过,因为心里根本没有“编译速度慢”这种概念。没有坏,哪来好?所谓矛盾的对立统一。遇到的第一个“慢”的编译器也许是javac,但因为Java的特殊性,也就容忍了。真正接触到世间的“恶势力”,还要算是第一次使用GCC的时候……准确地说是MinGW。开源世界曾给我诸多惊喜,其一就是原来编译器也可以这么慢的。那时我不禁对开源社区肃然起敬,他们就用这样的编译器,建立起了怎样一个多彩的世界!也在那时才明白了,Borland其实真的很了不起。
时至今日我也不是很了解Borland是怎么做到的,很久以来也不知道GCC是差在了哪里。然而……有一次心血来潮,忽然想看看 MinGW编译过程中加载的所有头文件。于是用了一下 -H 参数。结果是满意的,加载的头文件真多呀。接下来……开始感觉到另外的一些东西了。敢情,大部分编译时间是浪费在这里的呀?——“预编译头”的概念如鲸鱼般跃出脑海。
预编译头技术是在VC中第一次了解的,其对编译速度的提高,绝对给人以深刻的印象。使用MinGW的时候居然忘了这个古老的咒语。是否正是我所需要的?百度几下,结果令人失望,这方面的文献少得可怜,更令人沮丧的是还有不少人相信GCC是没有预编译头技术的。贼心不死的我打开 GCC官方文档,查找precompiled headers。慢着,居然如此顺利!——官方文档讨论篇幅并不长,但足以让我喊万岁了~不用多,一句话就够了,怎么说来着?Simply compile it!
所谓预编译头,就是把头文件事先编译成一种二进制的中间格式,供后续的编译过程使用。不要把这个中间格式与. o/.obj/.a/.lib的格式混淆,他们是截然不同的,所以预编译头文件的特性和目标文件也不同(尽管他们都属于某种中间文件)。——但也有类似的地方的,比如,它们都是编译器之间不兼容的^_^,就是说你不能把VC生成的预编译头拿到GCC上去用。甚至扩展名都不一样,VC的是大家都熟悉的. pch,而GCC的,是.gch——今天的主角。
为什么要使用预编译头?再明确不过了,提高编译速度。为什么会提高编译速度?这么说吧,你有两个文件a.cpp和b.cpp,都包含了同一个头文件c.h。那么正常的流程是:将c.h和a.cpp合并,编译成a.o;将c.h和b.cpp合并,编译成b.o;最后将a.o和b.o链接成可执行文件。过程很简单,浪费时间之处也一目了然:头文件c.h的内容实际上被解析了两遍。也许你要说,当然要两遍了,因为头文件几乎是不生成任何代码的,只有依附于具体的.cpp文件才有意义。正确,但那只是在代码执行过程中。但在代码编译的时候呢?编译器读入源代码,首先将其解析成为一种内部的表示方式。这个过程与其所依附的.cpp文件并无关系,编译器接着可以读入.cpp文件并同样解析成内部表示,然后把两段内部表示拼接起来,再进行接下来的操作。既然编译两个.cpp文件都要先对c.h进行解析,那干嘛不把c.h解析好了保存成临时文件,用时读入,不就可以省了一次解析的时间了吗?——预编译头技术节省时间的原理正在于此,尤其是在这样一个事实下:对源代码的“解析”这个步骤,确实是占了编译时间中很可观的一部分。
我看见你满是狐疑的脸:预处理,就是编译之前的处理,合并.h和.cpp文件分明是预处理的步骤,而解析源代码是编译之中的步骤,先解析后合并?怎么“预”处理反而跑到编译步骤之后了?这还叫“预”吗?——这个问题我们决定不深究了,毕竟现在的编译器早就混淆了预处理与编译的界限……毕竟,这么做是管用的,对吗?
我们来看看结果。写一个C++的Hello world,使用cout输出一行字。包含了什么头文件?当然是iostream。这个头文件对于人们来说,绝对是熟视无睹级别的。然而使用它的时候,你注意到编译器幕后的累累“罪行”了吗?是的,用 -H 参数编译一下这个Hello world吧!看看总共加载了多少个头文件?我的机器上,总共103个!
是的,你应该将它们做成一个.gch文件。如何做?如前所述,再简单不过:只要编译它就可以了:
g++ xxx.h
一句话,就是:把.h文件当成.cpp文件一样来编译。这是最简单的,如果需要控制编译细节,比如常量定义之类,大可加上其它选项。运行之后,你会发现同个目录里生成了一个名叫xxx.h.gch的文件,这就是我们要的。也许你和我一样,迫不及待地尝试g++ iostream了?呵呵,结果一定是和我一样的失败——在编译.gch的过程中,GCC并没有使用环境变量或 -I 选项来查找被编译的头文件,被编译的头文件必须在当前目录下。然而,被编译的头文件所进一步包含的其它头文件,却可以通过以上途径找到。简言之,就是把直接编译的那个头文件以类似对待.cpp文件的方式处理了。现在知道该如何编译iostream了吧?对,在当前目录里建立一个头文件,起个随你喜欢的名字,比如foo.h,在其里面写上:#include <iostream>,然后编译它:g++ foo.h。生成的foo.h.gch,就是我们要的了。其它文件需要用到iostream的,不要包含iostream,要包含foo.h。切记,不是去包含foo.h.gch!
如果你用过VC,那么这个foo.h也许会让你找到一种似曾相识的感觉吧?对了,就相当于那个 stdafx.h!那么你也该记得,每个文件包含这个foo.h,都应该在文件一开始的地方,否则会出错。真的,终于找到了GCC中的stdafx.h,这种感觉几乎让人热泪盈眶了^_^
那么接下来,照搬一些stdafx.h相关的注意事项吧,它们同样适用于.gch文件:应该把那些不常修改的(首当其冲,当然是系统的)头文件放在预编译头里,而那些属于你的程序的一部分的头文件,一般并不放在预编译头里,因为它们可能随时要被修改的。每修改一次就要重新生成预编译头,并没有速度优势可言,失去预编译头的意义了。另外重要的注意事项是:如果你生成预编译头的时候用了一些选项,比如宏定义,那么使用这个预编译头的其它源代码文件,被编译的时候也要使用这些选项,否则会因为不匹配而编译失败。
对了,说了半天,从来没有正面讲过如何使用已经生成的预编译头。然而看到这里也该明白了,是的,很简单,只要包含其所对应的.h文件即可!比如你有个头文件叫foo.h,另外有一大堆其它文件都包含了这个foo.h,原来没有使用预编译头技术,现在忽然想使用了,于是把foo.h编译成了foo.h.gch。那其它文件要做怎样的修改?——什么都不用,一切照旧!聪明的GCC编译器在查找一个.h文件之前,会自动查找其目录里有没有对应的.gch文件,如有,且可用,则用之;没有,才用到真正的.h头文件。——慢着,“如有,且可用”,什么叫“可用”?——就是指这个.gch格式要正确,版本要兼容,而且如上所述,编译两者要用同样的选项。如果.gch不可用,编译器会给出一条警告,告诉我们:这个预编译头不能用!我只好用原有的.h 头文件啦!什么?你说看不到这个警告?——当然,要先打开 -Winvalid-pch 选项才行,其默认是关闭的。
用 -H 选项感受一下预编译头的清爽吧!再没有滚不完的头文件了,明显提高的速度,绝对会让你有种翻身解放的感觉,原来MinGW也可以和蜗牛般的速度说再见的。
相关文章:

Java项目:慢病报销管理信息系统(java+MySQL+Jdbc+Servlet+Jsp)
源码获取:博客首页 "资源" 里下载! 一、项目简述 功能: 慢病管理,医疗机构管理,家庭管理,费用交纳,费用报销,报表统计等等功能。 二、项目运行 环境配置: Jd…

Jetty Cross Origin Filter解决jQuery Ajax跨域访问的方法
当使用jQuery Ajax post请求时可能会遇到类似这样的错误提示 XMLHttpRequest cannot load http://xxxxxx. Origin http://xxxxxx is not allowed by Access-Control-Allow-Origin. 这是Ajax跨域访问权限的问题,服务器端不接受来自另一个不同IP地址的由脚本文件发出的…

php 遍历所有的文件
<?php prin_r(glob($path)); 2 转载于:https://www.cnblogs.com/zqk8553/p/3640071.html

Oracle数据库物理存储结构管理
1、实验目的 (1)掌握Oracle数据库数据文件的管理。 (2)掌握Oracle数据库控制文件的管理。 (3)掌握Oracle数据库重做日志文件的管理。 (4)掌握Oracle数据库归档管理。 2、实验环境 Wi…

KDE与GNOME的战争史(转载)
虽然在商业方面存在竞争,GNOME与KDE两大阵营的开发者关系并没有变得更糟,相反他们都意识到支持对方的重要性—如果KDE和GNOME无法实现应用程序的共享,那不仅是巨大的资源浪费,而且将导致Linux出现根本上的分裂。 KDE与GNOME是…

[ActionScript 3.0] AS向php发送二进制数据方法之——在URLRequest中构造HTTP协议发送数据...
主类 HTTPSendPHP.as 1 package 2 {3 import com.JPEGEncoder.JPGEncoder;4 import com.fylib.httpRequest.HttpRequestBuilder;5 import com.fylib.httpRequest.HttpRequestBuilderConsts;6 import flash.display.Bitmap;7 import flash.display.BitmapDa…

Java项目:校园招聘平台系统(java+MySQL+Jdbc+Servlet+SpringMvc+Jsp)
源码获取:博客首页 "资源" 里下载! 一、项目简述 功能: 用户和企业用户的注册登录,简历的筛选查看搜索,应聘信息互动等等。 二、项目运行 环境配置: Jdk1.8 Tomcat8.5 mysql Eclispe…

静态资源(StaticResource)和动态资源(DynamicResource)
静态资源(StaticResource)和动态资源(DynamicResource) 资源可以作为静态资源或动态资源进行引用。这是通过使用 StaticResource 标记扩展或 DynamicResource 标记扩展完成的。 StaticResource 通过替换已定义资源的值来为 XAML 属…
如何在 Kaggle 首战中进入前 10%(转)
如何在 Kaggle 首战中进入前 10%(转) 来源:https://dnc1994.com/2016/04/rank-10-percent-in-first-kaggle-competition/ Introduction 本文采用署名 - 非商业性使用 - 禁止演绎 3.0 中国大陆许可协议进行许可。著作权由章凌豪所有。 Kaggle …

一.Timesten安装
一,安装timesten IMDB并测试 1. 创建数据库相关用户和组 groupadd timestenuseradd -g timesten -G dba timestenpasswd timesten2. 创建相关目录 mkdir /etc/TimesTenchmod 775 /etc/TimesTenchown timesten:timesten /etc/TimesTenmkdir /u01/app/timesTen chmod …

linux 入门-1
刚开始接触linux,总有些简单的问题不知道怎么搞定,先将目前汇总的解决方法叫做"linux入门-1",后续在使用过程中逐步总结。 1. 连接 ADSL : sudo pon dsl-provider 断开 ADSL: sudo poff 查看 ADSL 状态&a…

Java项目:家庭理财系统(java+SSM+JSP+Tomcat8+Mysql)
源码获取:博客首页 "资源" 里下载! 一、项目简述 功能:家庭理财,财务分析,统计等等。 二、项目运行 运行环境: jdk8tomcat8mysqlIntelliJ IDEA( Eclispe , MyEclispe ,Sts 都支持,代…

ORA-19809: 超出了恢复文件数的限制
实验环境:Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod 实验背景:向tough.t中插入40万条记录,然后rollback,接着执行了shutdown abort命令。当重新启动数据库的时候遇到以下问题。 SQL> alter database …

VC manifest
manifest原理和用途 dll是被动态调用的,所以会被若干个程序共享使用的 但是如果dll在应用程序不知道的情况下升级了、或是被另一个程序更改了,就可能会出现问题,即”DLL Hell” 随着系统资源越来越丰富,硬盘不那么紧张࿰…

判断年月日是否正确
//输入年月日 Console.Write("请输入年:"); int year Convert.ToInt32(Console.ReadLine()); Console.Write("请输入月:"); int month Convert.ToInt32(Console.ReadLine()); Console.Write("请输入日:"); i…

Java项目:农业计算工具(java+swing)
源码获取:博客首页 "资源" 里下载! 1、换算:吨、千克、斤,晌/公顷、亩、平方米,晌/株、亩/株、平方米/株 2、籽粒干重、果穗干重、出籽率计算 3、发芽种粒数、供试种粒数、发芽率计算 4、种子袋、晌、亩、品种 换算 pac…

web网站加速之CDN(Content Delivery Network)技术原理
2019独角兽企业重金招聘Python工程师标准>>> 在不同地域的用户访问网站的响应速度存在差异,为了提高用户访问的响应速度、优化现有Internet中信息的流动,需要在用户和服务器间加入中间层CDN. 使用户能以最快的速度,从最接近用户的地方获得所需的信息&…

response.getWriter().write()和 response.getWriter().print()的区别
异步上传图片的代码。发现里面用了response.getWriter().print(),故联想到response.getWriter().writer(),经过一番api的查找与实操,总结如下: response.getWriter()返回的是PrintWriter,这是一个打印输出流。response…

c语言中volatile关键字的作用
读文章之前 可以先看一下《程序员的自我修养 》第28页 过度优化。 volatile 提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有 volatile关键字,则编…

Java项目:抽奖点名神器(HTML+可自定义抽选)
源码获取:博客首页 "资源" 里下载! 用于年终抽奖或随机点名神器 //获取页面元素var student_box document.getElementById("student_box");//循环生成HTMLvar html "";for (var i 0 ; i < 22; i ) {html <div st…

Hive Metastore 连接报错
背景 项目中需要通过一些自定义的组件来操控hive的元数据,于是使用了remote方式来存储hive元数据,使用一个服务后台作为gateway,由它来控制hive元数据。 现象 在windows上连接hive metastore的时候,无端的会报NullPointerExceptio…

普通的简单对话框
2019独角兽企业重金招聘Python工程师标准>>> activity_main.xml <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:layout_width"fill_parent"and…

[POI2007]ZAP-Queries
出处:http://www.cnblogs.com/peng-ym/p/8660937.html (还是 建议 去看 原文) 题目:链接:https://www.luogu.org/problemnew/show/P3455 #include<bits/stdc.h> #define LL long long #define ULL unsigned long…

python pdb 基础调试
当手边没有IDE,面对着python调试犯愁时,你就可以参考下本文;(pdb 命令调试)参考:http://docs.python.org/library/pdb.html和 (pdb)help首先你选择运行的 pypython -m pdb myscript.py(Pdb) 会自动停在第一行ÿ…

Java项目:设计管理系统(java+SSM+JSP+MYSQL+layui+Maven)
源码获取:博客首页 "资源" 里下载! 一、项目简述 功能包括: 课题管理,学生管理,内容管理,文件管理,提问管理,教师管理,进度管理等等。 二、项目运行 环境配置…

linux(以ubuntu为例)下Android利用ant自动编译、修改配置文件、批量多渠道,打包生成apk文件...
原创,转载请注明:http://www.cnblogs.com/ycxyyzw/p/4555328.html 之前写过一篇《windows下Android利用ant自动编译、修改配置文件、批量多渠道,打包生成apk文件》,关于ant打包原理,请先阅读此篇文章,再阅…

自制的一个eclipse 插件jcodesmith(代码生成器)
为什么80%的码农都做不了架构师?>>> 实现思路 使用模板输出,定制一些特定的输入【如数据库元素、普通文件】等控制模板生成;并把指定输入可视化,方便使用; 下载安装 插件下载:http://git.os…

Boring counting HDU - 3518 (后缀数组)
Boring counting \[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \] 题意 给出一个字符串,求出其中出现两次及以上的子串个数,要求子串之间不可以重合。 思路 对字符串后缀数组,然后枚举子串长度 \(len\),若某一段连续的 \…

什么是JTAG
JTAG(Joint Test Action Group)联合测试行动小组)是一种国际标准测试协议(IEEE 1149.1兼容),主要用于芯片内部测试。现在多数的高级器件都支持JTAG协议,如DSP、FPGA器件等。标准的JTAG接口是4线:TMS、 TCK、TDI、TDO&a…

Java项目:个人博客系统(java+SSM+Mysql+Servlet+JavaWeb)
源码获取:博客首页 "资源" 里下载! 一、项目简述 项目内容包括:首页,登陆,新建文章,搜索,登陆日志,登录次数,评论统计,相关信息,文章列…