什么是预编译?

本文详细介绍了预编译头的概念、作用、使用方法及其注意事项,旨在提高C/C++开发效率并减少代码重复编译的问题。通过手动方法和自动使用方式的对比,阐述了如何配置预编译头文件来加速编译过程,并提供了关键步骤及代码示例,以确保代码稳定性与高效性。

文章(一)

处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等

  就是为编译做的预备工作的阶段

  主要处理#开始的预编译指令

  预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。常见的预编译指令有:

  (1)#include 指令

  该指令指示编译器将xxx.xxx文件的全部内容插入此处。若用<>括起文件则在系统的INCLUDE目录中寻找文件,若用" "括起文件则在当前目录中寻找文件。一般来说,该文件是后缀名为"h"或"hpp"的头文件。

  注意:<>不会在当前目录下搜索头文件,如果我们不用<>而用""把头文件名扩起,其意义为在先在当前目录下搜索头文件,再在系统默认目录下搜索。

  (2)#define指令

  该指令有三种用法:

  第一种是定义标识,标识有效范围为整个程序,形如#define XXX,常与#if配合使用;

  第二种是定义常数,如#define max 100,则max代表100(这种情况下使用const定义常数更好,原因见注1);

  第三种是定义"函数",如#define get_max(a, b) ((a)>(b)?(a):(b)) 则以后使用get_max(x,y)就可以得到x和y中较大的数(这种方法存在一些弊病,见注2)。

  (3)#if、#else和#endif指令

  这些指令一般这样配合使用:

  #if defined(标识) //如果定义了标识

  要执行的指令

  #else

  要执行的指令

  #endif

  在头文件中为了避免重复调用(比如说两个头文件互相包含对方),常采用这样的结构:

  #if !(defined XXX) //XXX为一个在你的程序中唯一的标识符,

  //每个头文件的标识符都不应相同。

  //起标识符的常见方法是若头文件名为"abc.h"

  //则标识为"abc_h"

  #define XXX

  真正的内容,如函数声明之类

  #endif

  注1:因为:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)。

  注2:例如get_max(a++, b)时,a++会被执行多少次取决于a和b的大小!所以建议还是用内联函数而不是这种方法提高速度。虽然有这样的弊病,但这种方法的确非常灵活,因为a和b可以是各种数据类型。

 

 

 

文章(二)

预编译头的概念:

所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的C/C++代码--------甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不会被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-7M大。注意及时清理那些没有用的预编译头文件。

也许你会问:现在的编译器都有Time stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东西(.eg Macro, Preprocesser )都要重新处理一遍。VC的预编译头文件保存的正是这部分信息。以避免每次都要重新处理这些头文件。

预编译头的作用:

方法一:手动方法

根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然就提高了。

预编译头的使用:

要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不会经常改变的代码和其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件)

想必大家都知道 StdAfx.h这个文件。很多人都认为这是VC提供的一个“系统级别”的,编译器带的一个头文件。其实不是的,这个文件可以是任何名字的。我们来考察一个典型的由AppWizard生成的MFC Dialog Based 程序的预编译头文件。(因为AppWizard会为我们指定好如何使用预编译头文件,默认的是StdAfx.h,这是VC起的名字)。我们会发现这个头文件里包含了以下的头文件:

#include <afxwin.h>     // MFC core and standard components

#include <afxext.h>     // MFC extensions

#include <afxdisp.h>   // MFC Automation classes

#include <afxdtctl.h>       // MFC support for Internet Explorer 4 Common Controls

#include <afxcmn.h>  

这些正是使用MFC的必须包含的头文件,当然我们不太可能在我们的工程中修改这些头文件的,所以说他们是稳定的。

那么我们如何指定它来生成预编译头文件。我们知道一个头文件是不能编译的。所以我们还需要一个cpp文件来生成.pch 文件。这个文件默认的就是StdAfx.cpp。在这个文件里只有一句代码就是:#include “Stdafx.h”。原因是理所当然的,我们仅仅是要它能够编译而已―――也就是说,要的只是它的.cpp的扩展名。我们可以用/Yc编译开关来指定StdAfx.cpp来生成一个.pch文件,通过/Fp编译开关来指定生成的pch文件的名字。打开project ->Setting->C/C++ 对话框。把Category指向Precompiled Header。

在图中我们的Project Options(右下角的那个白的地方)可以看到 /Fp “debug/PCH.pch”,这就是指定生成的.pch文件的名字,默认的通常是 <工程名>.pch(我的示例工程名就是PCH)。

这时原来的Project Option变成了 Source File Option(原来是工程,现在是一个文件,当然变了)。在这里我们可以看到 /Yc开关,/Yc的作用就是指定这个文件来创建一个Pch文件。/Yc后面的文件名是那个包含了稳定代码的头文件,一个工程里只能有一个文件的可以有YC开关。VC就根据这个选项把 StdAfx.cpp编译成一个Obj文件和一个PCH文件。


在这里,Precomplier 选择了 Use ………一项,头文件是我们指定创建PCH 文件的stdafx.h

文件。事实上,这里是使用工程里的设置,/Yu”stdafx.h”。

这样,我们就设置好了预编译头文件。也就是说,我们可以使用预编译头功能了。以下是注意事项:

1):如果使用了/Yu,就是说使用了预编译,我们在每个.cpp文件的最开头,我强调一遍是最开头,包含 你指定产生pch文件的.h文件(默认是stdafx.h)不然就会有问题。如果你没有包含这个文件,就告诉你Unexpected file end. 如果你不是在最开头包含的,你自己试以下就知道了,绝对有很惊人的效果…..

2)如果你把pch文件不小心丢了,根据以上的分析,你只要让编译器生成一个pch文件就可以了。也就是说把 stdafx.cpp(即指定/Yc的那个cpp文件)从新编译一遍就可以了。当然你可以傻傻的 Rebuild all。简单一点就是选择那个cpp文件,按一下Ctrl + F7就可以了。

方法二。自动使用

很简单只要指定/YX就可以了。或者在上图中选择Automatic………就可以了。注意的事情是如果你指定了/Yc /Yu的话,/Yx是会被忽略的。前者的优先级别高一些。

 

 

文章(三)

一、什么是预编译头?
预编译头物理上与通常的的.obj文件是一样的,但编译入预编译头的.h,.c,.cpp文件在整个编译过程中,只编译一次,如预编译头所涉及的部分不发生改变的话,在随后的编译过程中此部分不重新进行编译。进而大大提高编译速度,并便于对头文件进行管理,也有助于杜绝重复包含问题。

二、什么时候使用预编译头?
当大多.c或.cpp文件都需要相同的头文件时。
当某些代码被大量重复使用时。
当导入某些不同库都有实现的函数,并产生混乱时。

<think>好的,我现在需要回答用户关于预编译的概念及其使用场景的问题。首先,我需要明确预编译的定义,然后列举需要进行预编译的情况。根据用户提供的引用资料,我需要综合各个来源的信息,确保回答准确且覆盖所有相关方面。 首先,预编译的定义。根据引用[1],预编译程序对源程序进行替代工作,生成一个没有宏定义、条件编译指令和特殊符号的输出文件。这个文件与源文件含义相同但内容不同。这应该作为定义的核心部分。引用[4]提到C/C++中的预编译指令,比如#include的处理方式,这也支持预编译的过程是处理这些指令的阶段。 接下来,使用场景。引用[1]提到多个文件链接成一个文件时需要预编译。引用[2]指出C#中使用预编译指令来区分不同版本,禁止编译部分代码,用于发布不同版本或调试。引用[3]提到了预编译的正则表达式,可能是在Python中的优化手段。引用[4]详细说明了C/C++中#include的处理,区分标准库和用户文件。引用[5]讨论JDBC中的预编译SQL防止注入攻击。 需要将这些信息整合,分点列出使用场景:跨文件依赖处理、条件编译、代码优化、数据库查询优化、模板或配置预处理。每个点需要结合引用资料中的例子,比如C#版本控制、C/C++头文件包含、SQL预编译等。 同时要注意用户提供的引用中有不同语言的例子,如C#、C/C++、Python、Java,说明预编译在不同语境下的应用。需要指出预编译的两种理解:预处理阶段和预编译语句(如SQL),可能用户需要区分这两者,但根据问题,可能更关注预处理阶段。 最后,生成相关问题,需要基于回答内容,提出进一步的问题,比如预编译的具体步骤、不同语言的实现差异、优化效果等。</think>### 预编译的定义及使用场景 #### 1. 预编译的定义 预编译(预处理)是编译过程的第一步,主要对源代码进行文本替换和条件筛选,生成一个“纯净”的中间文件供后续编译使用。具体来说: - **处理宏定义、条件编译指令**(如`#define`、`#ifdef`)和特殊符号,生成无这些指令的中间文件[^1]。 - **包含文件展开**:将`#include`指令替换为对应文件内容(例如C/C++中标准库头文件`<iostream>`或用户自定义头文件`"myheader.h"`)[^4]。 - **消除注释和冗余代码**,为编译器提供标准化的输入[^1][^4]。 #### 2. 需要进行预编译的情况 以下场景通常需要预编译: ##### (1) **跨文件依赖处理** - **多文件项目整合**:将多个源文件链接成一个文件,简化编译流程(如C/C++项目的头文件包含)[^1][^4]。 - **避免重复定义**:通过预编译头文件(`.pch`文件)减少重复解析,提升编译速度。 ##### (2) **条件编译与版本控制** - **发布不同版本代码**:利用预编译指令(如C#的`#if DEBUG`)区分调试版和发布版,控制功能模块的编译[^2]。 - **平台适配**:根据操作系统或硬件条件编译不同代码段(例如`#ifdef WIN32`)。 ##### (3) **代码优化与调试** - **禁用冗余代码**:通过`#if 0`临时屏蔽代码块,避免删除代码即可调试[^2][^4]。 - **正则表达式预编译**:在Python等语言中,预编译正则表达式(如`re.compile`)提升运行时效率[^3]。 ##### (4) **数据库查询优化与安全** - **防止SQL注入**:在JDBC中使用预编译语句(`PreparedStatement`),将SQL逻辑与参数分离,既优化执行计划,又避免注入攻击[^5]。 ##### (5) **模板或配置预处理** - **动态生成代码**:根据预定义宏生成不同代码逻辑(如日志模块的调试信息开关)[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值