c++ 预编译

转载 :https://blog.csdn.net/sunshinewave/article/details/51020421

  1.   

程序的编译过程可以分为预处理、编译、汇编三部分,其中预处理是首先执行的过程,预处理过程扫描程序源代码,对其进行初步的转换,产生新的源代码提供给编译器。
预处理过程读入源代码之后,会检查代码里包含的预处理指令,完成诸如包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码的工作。下面介绍一些C/C++中预编译的指令。

一 #指令
预处理指令以#号开头,并且#号必须是该行除了任何空白字符外的第一个字符。

后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。

单纯一个#号表示空指令,没有任何作用。
二 #include指令

include预处理指令的作用是在指令处展开被包含的文件。展开被包含的文件之后,在代码就可以正常地调用该文件中所声明的变量和函数。#include指令有两种使用方法

include <xxx.h>

include “xxx.h”

第一种方法将待包含的头文件使用尖括号括起来,预处理程序会在系统默认目录或者括号内的路径查找,通常用于包含系统中自带的公共头文件。
第二种方法将待包含的头文件使用双引号引起来,预处理程序会在程序源文件所在目录查找,如果未找到则去系统默认目录查找,通常用于包含程序作者编写的私有头文件。
文件包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。在大型的程序中可能会产生重复包含的问题,如

include “a.h”

include “b.h”

一个程序包含了a.h和b.h两个头文件,但a.h和b.h可能同时又都包含了c.h,于是该程序就包含了两次c.h,这在一些场合下会导致程序的错误,可以通过下面的条件编译进行解决。
三 #define、#undef指令

define指令定义了一个标识符及一个串,标识符称为宏名,源程序中宏名的每次出现都会用其定义的串进行替换,称为宏替换。

undef指令取消一个已定义的宏。

宏一般使用大写字母定义,其可以出现在程序的任意地方。宏替换仅仅是以文本串代替宏标识符的过程,该过程很容易出现一些逻辑上的错误,需要仔细处理一些关于括号的问题。
以下代码用宏定义了一个常量PI,但C++中建议使用const进行常量定义,因为宏替换并不会进行类型匹配之类的安全性检查。同时用宏定义了一个MAX函数,其好处是没有函数调用的额外开销,运行速度较快,但容易出错,而且大量的宏替换会增加代码的长度。

  1. void test1()  
  2. {  
  3. #define PI 3.14  
  4. #define MAX(a,b) (((a) > (b)) ? (a) : (b))  
  5.     cout << PI << endl;         //3.14  
  6.     cout << MAX(2, 3) << endl;  //3  
  7. #undef PI  
  8.     //cout << PI << endl;       //编译出错  
  9. }  
void test1() 
{

define PI 3.14

define MAX(a,b) (((a) > (b)) ? (a) : (b))

cout &lt;&lt; PI &lt;&lt; endl;         //3.14
cout &lt;&lt; MAX(2, 3) &lt;&lt; endl;  //3

undef PI

//cout &lt;&lt; PI &lt;&lt; endl;       //编译出错

}

另外还有两个特殊的运算符:

宏定义中的#运算符把跟在其后的参数转换成一个字符串称为字符串化运算符。

宏定义中的##运算符把出现在##两侧的参数合并成一个符号。

  1. void test2()  
  2. {  
  3. #define CAT(n) “ABC”#n  
  4. cout << CAT(123) << endl;//ABC123  
  5. #define NUM(a,b) a##b  
  6. #define STR(a,b) a##b  
  7. cout << NUM(1, 2) << endl;//12  
  8. cout << STR(”Hello”“World”) << endl;//HelloWorld  
  9. }  
void test2()
{




define CAT(n) "ABC"#n

cout << CAT(123) << endl;//ABC123

define NUM(a,b) a##b

define STR(a,b) a##b

cout << NUM(1, 2) << endl;//12
cout << STR("Hello", "World") << endl;//HelloWorld
}

四 #if、#elif、#else、#endif指令

这几个指令称为条件编译指令,可对程序源代码的各部分有选择地进行编译。
跟一般的if、else if、else语句类似,如果一个条件上的值为真,则编译它对应的代码,否则提过这些代码,测试下一个条件上的值是否为真。注意,作为条件的表达式是在编译时求值的,它必须仅含常量及已定义过的标识符,不可使用变量,也不可以含有操作符sizeof(sizeof也是编译时求值)。
命令#endif标识一个#if块的结束。

  1. void test3()  
  2. {  
  3. #define OPTION 2  
  4.   
  5. #if OPTION == 1  
  6. cout << ”Option: 1” << endl;  
  7. #elif OPTION == 2  
  8. cout << ”Option: 2” << endl; //选择这句  
  9. #else  
  10. cout << ”Option: Illegal” << endl;  
  11. #endif  
  12. }  
void test3() 
{

define OPTION 2

if OPTION == 1

cout << "Option: 1" << endl;

elif OPTION == 2

cout << "Option: 2" << endl; //选择这句

else

cout << "Option: Illegal" << endl;

endif

}
五 #ifdef、#ifndef、#endif指令
这几个也是条件编译指令,其检查后面指定的宏是否已经定义,然后根据检查结果选择是否要编译后面语句。其中#ifdef表示”如果有定义“,#ifndef表示”如果没有定义“。
这个通常可以用于防止重复包含头文件的问题,如下所示:

  1. #ifndef MYHEAD_H  
  2.   
  3. #define MYHEAD_H  
  4. #include “myHead.h”  
  5.   
  6. #endif  
#ifndef MYHEAD_H

define MYHEAD_H

include "myHead.h"

endif

六 #line指令
C语言中可以使用FILE表示本行语句所在源文件的文件名,使用LINE表示本行语句在源文件中的位置信息。#line指令可以重新设定这两个变量的值,其语法格式为

line number[“filename”]

其中第二个参数文件名是可省略的,并且其指定的行号在实际的下一行语句才会发生作用。

  1. void test4()  
  2. {  
  3. cout << ”Current File: ” << FILE << endl; //Current File: d:\test.cpp  
  4. cout << ”Current Line: ” << LINE << endl; //Current Line: 48  
  5. #line 1000 “wrongfile”  
  6. cout << ”Current File: ” << FILE << endl; //Current File: d:\wrongfile  
  7. cout << ”Current Line: ” << LINE << endl; //Current Line: 1001  
  8. }  
void test4() 
{
cout << "Current File: " << FILE << endl; //Current File: d:\test.cpp
cout << "Current Line: " << LINE << endl; //Current Line: 48

line 1000 "wrongfile"

cout << "Current File: " << FILE << endl; //Current File: d:\wrongfile
cout << "Current Line: " << LINE << endl; //Current Line: 1001
}七 #error指令

error指令在编译时输出编译错误信息,可以方便程序员检查出现的错误。
  1. void test5()  
  2. {  
  3. #define OPTION 3  
  4. #if OPTION == 1  
  5. cout << ”Option: 1” << endl;  
  6. #elif OPTION == 2  
  7. cout << ”Option: 2” << endl;  
  8. #else  
  9. #error ILLEGAL OPTION! //fatal error C1189: #error :  ILLEGAL OPTION!  
  10. #endif  
  11. }  
void test5()

{

define OPTION 3

if OPTION == 1

cout << "Option: 1" << endl;

elif OPTION == 2

cout << "Option: 2" << endl;

else

error ILLEGAL OPTION! //fatal error C1189: #error : ILLEGAL OPTION!

endif

}
八 #pragma指令
该指令用来来设定编译器的状态或者是指示编译器完成一些特定的动作,它有许多不同的参数。
1. #pragma once
在头文件的最开始加入这条指令可以保证头文件只被编译一次。它可以实现上述使用#ifndef实现不重复包含头文件同样的功能,但可能会有部分编译系统不支持。

2. #pragma message
该指令能够让编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。其使用方法为:#pragma message(“消息文本”)
通过这条指令我们可以方便地记录在是否在源代码中定义过某个宏,如

  1. #define ISPC  
  2. #ifdef ISPC  
  3. #pragma message(“Macro ISPC is defined”) //编译输出:Macro ISPC is defined  
  4. #endif  
#define ISPC

ifdef ISPC

pragma message("Macro ISPC is defined") //编译输出:Macro ISPC is defined

endif

  1. #pragma warning
    该指令能够控制编译器发出警告的方式,其用法举例如:#pragma warning(disable : 4507 34; once : 4385; error : 164)
    这个指令有三部分组成,其中disable部分表示忽略编号为4507和34的警告信息,once部分表示编号为4385的警告信息只显示一次,error部分表示把编号为164的警告信息当做错误。
    另外,其还有两个用法

pragma warning(push [, n]):保存所有警告信息的现有的警告状态,后面n是可选的,表示把全局警告等级设为n。

pragma warning(pop):弹出最后一个警告信息,取消在入栈和出栈之间所作的一切改动。

具体例如如下:

  1. void test6()  
  2. {  
  3. #pragma warning(push) //保存编译器警告状态  
  4. #pragma warning(disable:4305) //取消4305的警告  
  5. bool a = 5; //无警告信息  
  6. #pragma warning(pop) //恢复之前的警告转改  
  7. bool b = 5; //warning C4305: ‘initializing’ : truncation from ‘int’ to ‘bool’  
  8. }  
void test6() 
{

pragma warning(push) //保存编译器警告状态

pragma warning(disable:4305) //取消4305的警告

bool a = 5; //无警告信息

pragma warning(pop) //恢复之前的警告转改

bool b = 5; //warning C4305: 'initializing' : truncation from 'int' to 'bool'
}4. #pragma comment
该指令将一个注释记录放入一个对象文件或可执行文件中。其使用方法为:#pragma comment(comment-type ,[“commentstring”])
其中comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker之一。常用的是lib关键字,可以帮我们连入一个库文件。 如

pragma comment(lib, “my.lib”)

  1. #pragma hdrstop
    该指令表示预编译头文件到此为止,后面的头文件不进行预编译。
  2. #pragma resource
    该指令表示把指定文件中的资源加入工程,如

pragma resource “*.dfm”

  1. #pragma code_seg
    该指令能够设置程序中函数代码存放的代码段,开发驱动程序的时候会使用到。使用方法为:#pragma code_seg([“section-name” [,”section-class”] ])。

  2. #pragma data_seg
    该指令建立一个新的数据段并定义共享数据。一般用于DLL中,在DLL中定义一个共享的有名字的数据段,这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量。其使用方法为:
    1. <pre name=“code” class=“cpp”>#pragma data_seg(“MyData”)  
    2. int value; //共享数据  
    3. #pragma data_seg()  
    <pre name="code" class="cpp">#pragma data_seg("MyData") 
    int value; //共享数据

pragma data_seg()

 
  1. #pragma pack

该指令规定数据在内存中的对齐长度,具体可以参考这里。

  1. #pragma pack(1)  
  2. struct S{char a; int b; };  
  3. void test7(){ cout << sizeof(S) << endl; } //5  
#pragma pack(1)
struct S{char a; int b; };
void test7(){ cout << sizeof(S) << endl; } //5



预编译头文件今天在改一个很大的程序,慢慢看,慢慢改。突然发现一个.c文件,里面什么也没有,就几个头文件,我一看,我靠,这不是把简单的问题搞复杂了吗,随手删掉那个c文件。结果不能编译了,我靠:fatal error C1083: Cannot open precompiled header file: \'Debug/v13_3.pch\':No such file or directory怎么rebuild all都不行。上网查了一下,才搞懂了:----------------总结------如果工程很大,头文件很多,而有几个头文件又是经常要用的,那么1。把这些头文件全部写到一个头文件里面去,比如写到preh.h2。写一个preh.c,里面只一句话:#include "preh.h"3。对于preh.c,在project setting里面设置creat precompiled headers,对于其他.c文件,设置use precompiled header file//哈哈我试了一下,效果很明显,不用precompiled header,编译一次我可以去上个厕所,用precompiled header,编译的时候,我可以站起来伸个懒腰,活动活动就差不多啦---------转载的文章----------预编译头的概念:所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.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 // MFC core and standard components#include // MFC extensions#include // MFC Automation classes#include // MFC support for Internet Explorer 4Common Controls#include <br
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值