现在的位置: 首页 > 综合 > 正文

【笔记】C++预处理命令

2014年02月02日 ⁄ 综合 ⁄ 共 5123字 ⁄ 字号 评论关闭

预处理命令是提供给编译器使用的,在对代码进行编译之前,先对预处理命令进行操作:

主要包括:文件引入#include,新的引入头文件不再加".h";

宏定义#define

编译选择,如#ifdef ,#else,#endif等。

1.(预编译头机制)

VC.NET 默认情况下使用预编译头(/Yu),不明白的在加入新.h文件后编译时总出现fatal error C1010: 在查找预编译头指令时遇到意外的文件结尾的错误。解决方法是在include头文件的地方加上#include "stdafx.h",或者打项目属性,找到“C/C++”文件夹,单击“预编译头”属性页。修改“创建/使用预编译头”属性为“不使用预编译头”。

2.预定义标识符

为了处理一些有用的信息,预处理定义了一些预处理标识符,虽然各种编译器的预处理标识符不尽相同,但是他们都会处理下面的4种:

__FILE__ 正在编译的文件的名字

__LINE__ 正在编译的文件的行号

__DATE__ 编译时刻的日期字符串,例如: "25 Jan 2006"

__TIME__ 编译时刻的时间字符串,例如: "12:30:55"

例如:cout<<"The file is :"<<__FILE__"<<"! The lines is:"<<__LINE__<<endl;

3.C/C++头文件一览

C、传统 C++

#include <assert.h>    //设定插入点

#include <ctype.h>     //字符处理

#include <errno.h>     //定义错误码

#include <float.h>     //浮点数处理

#include <fstream.h>    //文件输入/输出

#include <iomanip.h>    //参数化输入/输出

#include <iostream.h>   //数据流输入/输出

#include <limits.h>    //定义各种数据类型最值常量

#include <locale.h>    //定义本地化函数

#include <math.h>     //定义数学函数

#include <stdio.h>     //定义输入/输出函数

#include <stdlib.h>    //定义杂项函数及内存分配函数

#include <string.h>    //字符串处理

#include <strstrea.h>   //基于数组的输入/输出

#include <time.h>     //定义关于时间的函数

#include <wchar.h>     //宽字符处理及输入/输出

#include <wctype.h>    //宽字符分类

标准 C++ (同上的不再注释)

#include <algorithm>    //STL 通用算法

#include <bitset>     //STL 位集容器

#include <cctype>

#include <cerrno>

#include <clocale>

#include <cmath>

#include <complex>     //复数类

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <ctime>

#include <deque>      //STL 双端队列容器

#include <exception>    //异常处理类

#include <fstream>

#include <functional>   //STL 定义运算函数(代替运算符)

#include <limits>

#include <list>      //STL 线性列表容器

#include <map>       //STL 映射容器

#include <iomanip>

#include <ios>       //基本输入/输出支持

#include <iosfwd>     //输入/输出系统使用的前置声明

#include <iostream>

#include <istream>     //基本输入流

#include <ostream>     //基本输出流

#include <queue>      //STL 队列容器

#include <set>       //STL 集合容器

#include <sstream>     //基于字符串的流

#include <stack>      //STL 堆栈容器    

#include <stdexcept>    //标准异常类

#include <streambuf>    //底层输入/输出支持

#include <string>     //字符串类

#include <utility>     //STL 通用模板类

#include <vector>     //STL 动态数组容器

#include <cwchar>

#include <cwctype>

using namespace std;

C99 增加

#include <complex.h>   //复数处理

#include <fenv.h>    //浮点环境

#include <inttypes.h>  //整数格式转换

#include <stdbool.h>   //布尔环境

#include <stdint.h>   //整型环境

#include <tgmath.h>   //通用类型数学宏

4.编译控制指令

这些指令的主要目的是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。使用格式,如下:

1、如果identifier为一个定义了的符号,your code就会被编译,否则剔除。

#ifdef identifier

your code

#endif

2、如果identifier为一个未定义的符号,your code就会被编译,否则剔除。

#ifndef identifier

your code

#endif

3、如果expression非零,your code就会被编译,否则剔除。

#if expression

your code

#endif

4、如果identifier为一个定义了的符号,your code1就会被编译,否则your code2就会被编译。

#ifdef identifier

your code1

#else

your code2

#endif

5、如果epression1非零,就编译your code1,否则,如果expression2非零,就编译your code2,否则,就编译your code3。

#if expressin1

your code1

#elif expression2

your code2

#else

your code3

#enif

其他预编译指令除了上面我们说的集中常用的编译指令,还有3种不太常见的编译指令:#line、#error、#pragma,我们接下来就简单的谈一下。

#line的语法如下:

#line number filename

例如:#line 30 a.h

其 中,文件名a.h可以省略不写。这条指令可以改变当前的行号和文件名,例如上面的这条预处理指令就可以改变当前的行号为30,文件名是a.h。初看起来似 乎没有什么用,不过,他还是有点用的,那就是用在编译器的编写中,我们知道编译器对C++源码编译过程中会产生一些中间文件,通过这条指令,可以保证文件 名是固定的,不会被这些中间文件代替,有利于进行分析。

#error语法如下:

#error info

例如:

#ifndef UNIX

#error This software requires the UNIX OS.

#endif

这条指令主要是给出错误信息,上面的这个例子就是,如果没有在UNIX环境下,就会输出This software requires the UNIX OS.然后诱发编译器终止。所以总的来说,这条指令的目的就是在程序崩溃之前能够给出一定的信息。

#pragma是非统一的,他要依靠各个编译器生产者。例如VC++中:

#pragma comment(lib,"dllTest.lib")

导入库dllTest.lib。

 

注:预编译头机制

这里介绍VC6的预编译功能的使用,由于预编译详细使用比较的复杂,这里只介绍几个最重要的预编译指令: /Yu, /Yc,/Yx,/Fp。其它的详细资料可以参考:

MSDN -> Visual Studio 6.0 Document -> Visual C++ 6.0 Document -> VC++ Programmer Guider -> Compiler and Linker -> Details -> Creating Precompiled Header files

预编译头的概念:

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

也许你会问:现在的编译器都有 Time stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还 要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东 西(.eg Macro, Preprocessor )都要重新处理一遍。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。在左边的树形视图里选择整个工程。

我们可以使用预编译头功能了。以下是注意事项:

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

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


抱歉!评论已关闭.