《VC(VisualC++)预处理及编译.docx》由会员分享,可在线阅读,更多相关《VC(VisualC++)预处理及编译.docx(70页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、预处理器特殊术语在本书中,名词“参量”指的是传送给个函数的实体。有时候,它用 “actuaF联“ forma修饰,它们分别用于表示函数调用时的参量表达式和 在函数定义时的参量说明。名词“变量指的是种简单的C类型数据对 象,名词“对象”指的是C+对象和变量;它是个含义广泛的名词。翻译阶段C和C+程序由一个或多个源文件组成,它们都包含了程序的某些文本, 个不包含代码部分的源文件和它的包含文件(用#indude预处理器指令 包含的文件),若被条件编译指令(比如#if)调用,则称其为个“转换单 元”。源文件可被翻译多次,翻译过去的文件事实上是很正常的。已经翻译了的 翻译单元可保存在单独的对象文件或对象
2、代码库里,这些单个的转换单 元可被连接形成一个可执行文件或动态链接库(DLL)。转换单元可采用下列形式通信: 调用具有外部连接的函数。 调用具有外部连接的类成员函数。 直接更改具有外部连接的对象。文件的直接更改。内部外理通信(仅限于基于Microsoft Windows的应用程序)。以下是编 译器翻译文件的各个阶段:字符映射源文件中的字符被映射为内部源代码的形式。此阶段三字母序列被转 换为单字符的内部表现形式。行拼接在此阶段,源文件中所有以反斜杠()结尾且其后紧跟换行符的行,将 与下一行连接,从而由物理行生成逻辑行。所有非空源文件结束于个前 面没有反斜杠的换行符。语言符号化此阶段源文件被分为预
3、处理语言符号和空白字符。源文件中每个注释被 用个空白字符代替。换行符被保留。预处理此阶段执行预处理指令并将宏扩展至源文件,#include语句调用对所有 包括文本启动前面三个翻译步骤开头的翻译过程。字符集映射 所有的源字符集成员和转义序列将转换为执行字符集中的等价形式,对 于Microsoft C和C+来说,源字符集和执行字符集都是ASCI!码。字符串合并所有相邻的字符串和宽字符文字都将被合并。例如:StringconcatenatiorT 合 并为Stringconcatenation”。翻译所有的语言符号将按语法和语义规则进行分析;这些语言符号被转换为 目标代码。链接此阶段所有的外部引用被
4、分解以生成一个可执行程序或个动态链接 库。编译器在翻译过程中遇到语法错误时,将发出个警告或错误信息。链接器分解所有的外部引用,并把个或多个分开处理的转换单元和标 准库联接起来,以生成一个可执行程序或动态链接库(DLL)。预处理器指令预处理器指令如#define和#ifdef, 一般被用在不同的运行环境下,使源 程序易于更改和编译。源文件中的指令指示预处理器执行特有的行为。 例如,预处理器可替换文本中的语言符号,将其它的文件内容插入源文件 中,或移走文本的一部分以抑制文件中某部分的编译。预处理器行在宏扩 展之前被识别且执行。不过,如果宏扩展看起来象一个预处理器指令,该 命令将不能被预处理器识别。
5、除转义序列之外,预处理器语句采用与源文件语句相同的字符集。在预处 理器语句中的字符集和可执行程序的字符集是样的。预处理器也可识 别负字符值。预处理器可识别如下指令:#define #error #import #undef#elif #if #include#else #ifdef #line#endif #ifhdef #pragma数字符号片)是包含预处理器指令的行中的第一个非空白字符。空白字 符可出现在数字符号和指令的第一个字母之间。某些指令包含参量和 值。指令之后的任何文本(除作为指令一部分的参量或值之外)必须放在 单行注释分界符()之后或注释分界符(/*/)之间。预处理器指令可出现在
6、源文件的任何地方,但它们仅用于源文件的剩余 部分。#define指令可以用#define指令给程序中的常量取个有意义的名称,其语法的两种 形式如下:语法#define标识符语言符号字符串opt#define标识符(标识符。pt,标识符opt)语言符号字符串opt#define指令用语言符号字符串替换源文件中一个标识符的所有出现,标 识符仅在它形成个语言符号时被替换(参见“Microsoft Visual C+ 6.0 参考库”的“Microsoft Visual C+6.0语言参考手册”卷的第1章“词法规 定”中的“语言符号”)。例如,若标识符出现在个注释、个字符串或 作为个长标识符的一部分之
7、中,它就不被替换。个不带语言符号字符串的#define指令将移走源文件中每次标识符的 出现。标识符保留其定义且能用#defined和#ifdef测试。语言符号字符串参量由一系列语言符号组成,如关键字、常量或完整的语 句。一个或多个空白字符可将语言符号字符串和标识符分开。空白字符 不会被认为是被替换文本的一部分,文本最后语言符号之后的空白也不 会认为是替换文本的一部分。形式参数名称出现在语言符号字符串中以标志出实际值被替换的位置, 每个参数名称可在语言符号字符串中出现多次,且可以以任何次序出现。 调用时参量的数目必须与宏定义的参数数目相匹配。圆括号的自由运用 可确保正确地说明复杂的实际参量。 用
8、第二种语法形式可创建类似函数的宏。这种形式接受一个用圆括号括 起的可选参数表。在最初定义之后引用该标识符,可以使用实际参量替代 形式参数的语言符号字符串参量的形式,来替换标识符(标识符opt,标 识符opt)的每次出现。表中的形式参数必须用逗号隔开。该表中的每个名称都必须是唯一的, 且此参量表必须包括在圆括号中,标识符和左边的圆括号之间不能有空 格。对于占用多行的长指令可使用行连接,把反斜杠()放在换行符前。 形式参数名称的范围延伸到结束语言符号字符串的换行符。当一个宏以第二种语法形式定义时,参量表后的文本实例就构成一个宏 调用。在源文件中,一个标识符实例后的实际参量必须与宏定义的相应 形式参
9、数匹配。每个语言符号字符串之前无字符串化片)、字符化(#) 或语言符号粘贴併#)操作符,或其后无#操作符的形式参量,都被相应的 实际参量所替换。在指令替换形式参数之前,实际参量中的任何宏都将被 扩展(本章之后的“预处理器操作符中将介绍这些操作符)。以下带参量宏的例子说明了#define语法的第二种形式:定义光标行的宏#define CURSOR(top,bottom) (top) 8) | bottom)获取指定范围中的一个随机整数的宏#define getrandom(min,max) (rand()%(int)(max)+ l)-(min)+(min) 有副作用的参量有时会导致宏产生不希望
10、的结果。个给定的形式参量 在语言符号字符串中可能出现多次。如果该形式参数被个有副作用的 表达式所替换,则该表达式及其副作用,可能被求值多次(参见本章后面 “语言符号粘贴操作符#中的例子)。#undef指令可使个标识符的预处理器定义失效。有关的更多信息参见 #undef指令。若一个被定义的宏名称出现在语言符号字符串中(即使是 另一个宏扩展的结果),它将不被扩展。除非第二次定义(#define)宏与原定义完全相同,否则重定义个已定义 过的宏将产生一个错误信息。Microsoft特殊处Microsoft C/C+允许个宏的重定义,但会产生一个警告信息,说明新的 定义与原定义相同。ANSI C认为宏的
11、重定义是错误的。例如,下面的宏 对C/C+是相同的,但会产生一个警告信息:#define test(fl,f2) (fl*f2)#define test(al,a2) (al*a2)Microsoft特殊处结束这个例子用于说明#define指令:#define WIDTH 80 #define LENGTH (WIDTH+10) 第一个说明定义标识符WIDTH为整形常量80,且用WIDTH和整形常量 10定义LENGTH。LENGTH的每次出现都用(WIDTH+I0)所替换,接 着,WIDTH+IO的每次出现都用表达式(80+10)替换。WIDTH+10的圆括 号非常重要,因为它们决定着如下语
12、句的解释。va k LENGTH*20;经预处理后该语句变为:va k(80+10)*20;求得值为1800,若无括号,结果为:var=80+10*20 其值为 280Microsoft特殊处在文件开头用/D编译器选项定义宏和常量,和用个#define预处理指令 效果是样的。能用/D选项定义的宏可达30个。Microsoft特殊处结束#error指令采用error指令可产生编译错误信息。语法#error语言符号字符串 错误消息包括语言符号字符串参量,且从属于宏扩展。这些指令对于检测 程序的前后矛盾和预处理时的违犯约束是非常有用的,以下例子说明了 预处理时的出错处理:#if !defined(
13、_cplusplus)#error C+ compiler required.#endif当遇到#error指令时,编译终止。#if, #elif, #else 和#endif 指令#if、#elif、#else和#endif指令控制源文件中某部分的编译。如果表达式 併if之后)有一个非值,则紧跟在#if指令之后的行组将保留在转换单元 中。语法条件的:if部分elif部分opt else部分opt endif行if部分:if行文本if行:#if常量表达式#ifhdef标识符elif部分:elif行文本elif部分elif行 文本elif 行:#elif常量表达式else部分:else行文本el
14、se 行:#elseendif 行:#endif源文件中每个#if指令都必须与最近的个#endif相匹配。在#if和#endif 指令之前的#elif指令的数目是不限的,但最多只能有一个#else指令。#else必须是#endif之前的最后个指令。#if、#elif、#else和#endif指令 可嵌套在其它#if指令的文本部分。每个嵌套的#else、#elif或#endif指 令应属于前面最近的个#if指令。所有的条件编译指令,如#if和#ifdef,必须与文件结束前最近的#endif指 令匹配;否则,将产生一个错误消息。当条件编译指令包括在包含文件中 时,他们必须满足相同的条件:在包含文件
15、结尾没有不匹配的条件编译指 令。宏替换应在#elif命令后的命令行部分内进行,因此一个宏调用可用在常 量表达式中。预处理器选择个给定文本的出现之一作进步的处理。文本中指定的 个块可以是文本的任何序列。它可能占用一行以上。通常该文本是对 于编译和预处理器有意义的程序文本。预处理器处理选择文本并将其传送给编译器。若该文本包含预处理器指 令,预处理器将执行这些指令。编译器只编译预处理器选定的文本块。预处理器通过求值每个#if或#elif指令之后的常量表达式直到找到个 为真(非0)的常量表达式来选择单个文本项。预处理器选择所有的文本 (包括以#开头的其它预处理器指令)直到它关联的#elif、#else
16、或#endif。 如果常量表达式的所有出现都为假,或者如果没有#elif指令,预处理器将 选择#else后的文本块。如果#else被忽略,且所有#if块中的常量表达式 都为假,则不选择任何文本块。常量表达式是一个有以下额外限制的整型常量表达式: 表达式必须是整型且可以包括整型常量,字符常量和defined操作符。 表达式不能使用sizeof或一个类型造型操作符。 目标环境不能表示整数的所有范围。 在翻译表示中,int类型和10ng类型以及unsigned int类型和unsigned long类型是相同的。 翻译器可将字符常量翻译成一组不同于目标环境的代码值。为了确定 目标环境的属性,应在为目
17、标环境建立的应用程序中检测LIMITS.H的 宏值。 表达式不需执行所有的环境查询,但需与目标计算机的执行过程细节 隔离开。预处理器操作符defined可用于特殊的常量表达式,语法如下:语法defined(标识符)defined标识符若此标识符当前已定义,则该常量表达式被认为是真(非);否则,条件为 假(0)。个定义为空文本的标识符可认为已定义。defined指令只能用 于#if和#endif指令。在如下例子中,#if和#endif指令控制着三个函数调用中某个的编译: if defined (CREDIT)credit();#elifdefined(DEBIT)debit();#elsepri
18、nterror();#endif若标识符CREDIT已定义,则对于credit的函数调用被编译。若标识符 DEBIT被定义,则对于debit的函数调用被编译。若未定义任何标识符, 将编译对于printerror的函数调用。注意,在C和C+中,CREDIT和credit是不同的标识符,因为它们的大 小写不一样。如下例子中的条件编译语句给出了一个名称为DLEVEL的已定义的符 号常量: define SIGNAL 1 if STACKUSE = 1#define STACK 200#else#define STACK 100#endif#else#define SIGNAL 0#ifSTACKUS
19、E=l#define STACK 100#else#define STACK 50#endif#endif#ifDLEVEL=0#define STACK 0#elifDLEVEL=l#define STACK 100#elifDLEVEL5 display(debugptr) 浒 else#endif第一个#if块中有两组嵌套的#if、#else和#endif指令。第一组指令仅当 DLEVEL15为真时执行;否则,执行#else之后的语句。第二组中的#elif和#else指令选择基于DLEVEL值的四个选项之。常 量STACK依据DLEVEL定义为, 100或20。若DLEVEL大于5,则
20、编译语句:#elifDLEVEL5display(debugptr);且此时不定义STACK条件编译一般用于防止同一头文件的多重包含。C+中在头文件内经 常定义类的位置,可使用如下结构来防止多次定义。/EXAMPLE.H例子头文件#if !defined(EXAMPLE_H)#define ExampleE Hclass Example;#endif /!defined(EXAMPLE_H)上面的代码用于检查符号常量EXAMPLE_H是否已定义。若已定义,该 文件就已被包括且不需再处理;如果未定义,常量EXAMPLE_H将被定义, 以标记EXAMPLE.H为已经处理。Microsoft特殊处条
21、件编译表达式被看作为signed long值,且这些表达式与C+中的表达 式采用相同的规则求值。例如,表达式:#ifOxFFFFFFFFL 1UL为真。Microsoft特殊处结束#ifdef 和 ifndef 指令#ifdef和#ifhdef指令与使用defined(标识符)操作符的作用是样的。语法#ifdef标识符#ifhdef标识符等同于#ifdefined标识符 #if !defined 标识符#if指令能用的任何地方都可以用#ifUef和#ifhdef指令。当标识符已被定义时,#ifdef标识符语句等同于#if 1;而当标识符未定义或用#undef指令 对其反定义时,该语句等同于#i
22、f 0。这些指令仅用于检查C或C+源代 码中是否出现该标识符,而不是用于检查C或C+源程序中该标识符的 说明。提供这几个指令只为了与该语言的老版本兼容。目前的趋势是偏向于采 用defined(标识符)定义常量表达式的#if指令。#ifhdef指令测试与#ifdef相反的条件。若标识符未定义(或已用#undef 反定义),其条件为真(非0);反之,条件为假(0)。Microsoft特殊处可以使用/D选项从命令行传送标识符,采用/D选项至多可以指定30个 宏。检查个定义是否存在是非常有用的,因为定义可从命令行传送。例 如:/prog.cpp#ifhdef test 这三个语句放在你的代码中#def
23、ine final#endifCL /Dtest prog.cpp 这是编译的命令Microsoft特殊处结束#import 指令C-H特殊处#import指令用于从个类型库中结合信息。该类型库的内容被转换为C+类,主要用于描述COM界面。语法#import ”文件名属性#import文件名属性属性:属性1,属性2,.属性1属性2 .文件名是个包含类型库信息的文件的名称。个文件可为如下类型之 个类型库(.TLB或.ODL)文件。 个可执行(.EXE)文件。 个包含类型库资源(如.OCX)的库文件(.DLL)。 个包含类型库的复合文档。 其它可被LoadTypeLib API支持的文件格式。文件
24、名之前可以有一个目录规格。文件名必须是个已存在文件的名 称。两种格式的区别是当路径未完全说明时,预处理器检索类型库文件 的顺序不同。动作语法格式引号格式这种格式让预处理器首先搜索与包含#import语句的文件同 一目录的类型库文件,然后在所有包括(include)该文件的目录中搜索,最 后在如下路径中搜索尖括号格式这种格式指示预处理器沿以下路径搜索类型库文件编译器在以下目录中搜索已命名的文件:1 .PATH环境变量路径表。2 . LIB环境变量路径表。3 .用/1(额外的包括目录)编译器选项指定的路径。#import可以任选地包 含个或多个属性。这些属性使编译器改变类型库头文件的内容。个 反斜
25、杠。)符可用在个单的#import语句中包含额外的行,例如:#import test.lib no_namespace rename(ldName,NewName)#import属性列出如下:excludehigh_method_prefixhigh_property_prefixesimplementationonlyinclude(.)injectstatementnamedguidsno_ auto_ excludeno_ implementationnonamespaceraw_ dispinterfacesraw_interfaces_ onlyraw_ method_prefixr
26、awnativetypesraw_property_prefixesrenamerename_ namespace#import指令可创建两个在C+源代码中重构类型库内容的头文件,第一 个头文件和用Microsoft接口定义语言(MIDL)编译器生成的头文件类似, 但有额外的编译器生成代码和数据。第一个头文件与类型库有相同的基 本名,其扩展名为.TLH。第二个头文件也有与类型库相同的基本名,其扩 展名为.TLI。它包括了编译器生成成员函数的实现,且被包含在併include) 的第一个头文件内。两个头文件都在用/F。(命名对象文件)选项指定的输出目录中。随后它们 被读出和编译,就像第一个头文件被
27、#include指令命名样。以下是伴随#import指令的编译器优化:头文件被创建时,将被分配与类库相同的时间标志。处理#import时,编译器首先测试头文件是否存在,是否过期。若条件为真,就不需重新创建。编译器延迟对于OLE子系统的初始化,直到碰到第一个#import命令。#import指令也可参与最小重建且可被置于个预编译头文件中。基本类型库头文件基本类型库头文件由七个部分组成:1 .头部固定正文:由注释、COMDEF.H(定义用在头部的些标准宏)的 include语句和其它繁杂的安装信息组成。2 .前向引用和类型定义:由象struct IMyinterface之类的结构说明和用于 些TK
28、IND_ALIAS项的类型定义组成。3 .灵敏指针说明:模块类_com_ptr_t是个封装接口指针和消除调用 AddRef、Release和Queryinterface函数需求的灵敏指针实现。此外,它 隐藏了创建一个新COM对象中的CoCreatelnstance调用。此部分采用 宏语句_COM_SMARTPTR TYPEDEF将COM接的类型定义创建为 _com_ptr_t模板类的模板特例化。例如,对于界面IFoo,.TLH文件包含有:_COM_SMARTPTR TYPEDEF(IFoo,_ _uuidof(IFoo);编译器将其扩展为:type def com ptr t com IIID
29、 propput和propputref,分别 采用以前缀Get、Put和PutRef命名的成员函数来说明。high_property_prefixes属性用于分别说明这三种属性方法的前缀。implementation_only 属性implementation only属性禁止.TLH头文件(基本头文件)的生成。这个 文件包括了所有用于展示类型库内容的说明。该.TLI头文件和wrapper 成员函数的实现,将被生成且包含在编译过程中。当指定该属性时,该.TLI头部的内容将和用于存放普通.TLH头部的内容 放在相同的名称空间。此外,该成员函数不会作为联编说明。implementation onl
30、y属性一般希望与no implementation属性配对使用, 以跟踪预编译头文件(PCH)之外的实现。个有no_implementation属性 的#import语句被置于用来创建peh的源区域中,结果PCH将被些源文 件所用。个带implementation_only属性的#import语句随后被用在PCH 区域之外。在一个源文件里只需用一次这种语句。这将生成不需对每个 源文件进行额外重编译的所有必要的wrapper成员函数。注意:个#import语句中的implementation_ only属性必须和相同类型库中no_implementation属性的另个#import语句配套使用。
31、否则,将产 生编译错误。这是因为带no implementation属性的#import语句生成的 wrapper类定义需要编译implementation_only属性生成的语句实现。include.)属性Include(名称1,名称2,)名称1第一个被强制包含的项名称2第二个被强制包含的项(如果必要)类型库可能包含在系统头部或其它类型库中定义的项的定义。#import 指令试图用自动排斥这些项来避免多重定义错误。若这些项已经被排斥, 象警告C4192所指出的那样,且它们不应该被排斥,则这个属性可用于禁 止自动排斥。该属性可带任意数目的参量,每个参量应是被包括的类型库 项的名称。inject
32、_statement 属性inject_ statement(source_ text) sourcetext被插入到类型库头文件的源文本。 inject_statement属性将其参量作为源文本插入类型库头部。此文本被置 于包括头文件中类型库内容的名称空间说明的起始处。named_guids 属性named guids属性让编译器定义和初始化模板LIBID MyLib、 CLSID MyCoClass IID My Interface 和 DIID MyDispInterface 的旧式格 式的GUID变量。no_implementation 属性该属性阻止.TLI头文件的生成,这个文件包含
33、wrapper成员函数的实现。 如果指定这个属性,则展示类型库项说明的.TLH头将生成没有一个 #include语句包括该.TLI头文件。该属性与implementation_only属性配套使用。no_auto_exclude 属性类型库可能包括在系统头部或其它类型库中定义的项的定义。#import 试图通过自动排斥这些项来避免多重定义错误。当这样做时,每个被排斥 的项都将生成一个C4192警告信息。你可禁止这个属性使用自动排斥。no_namespace 属性#import头文件中的类型库内容一般定义在个名称空间里。名称空间 的名称在原来IDL文件的library语句中指定。如果指定no_n
34、amespace 属性,编译器就不会生成这个名称空间。如果你想使用个不同的名称空间,应代替使用rename_namespace属 性。raw_dispinterfaces 丿禹性raw dispinterfaces属性让编译器生成一个低级wr叩per函数。该函数用 于调用 IDispatch:Invoke 和返回 HRESULT 错误代码的 dispinterface 方 法和属性。如果未指定此属性,则只生成高级wrapper,它在失败时丢弃该 C+异常。raw_interfaces_only 属性raw_interfaces_only属性禁止生成错误处理wrapper函数以及使用这些 wra
35、pper函数的 declspec(属性)说明。raw_interfaces_only属性也导致删除在命名non_property函数中的缺省 前缀。通常该前缀是raw_。若指定此属性,函数名称将直接从类型库中 生成。该属性只允许展示类型库的低级内容。raw_method_prefix 属性raw_method_prefix(Prefix)Prefix被使用的前缀用raw_作为缺省前缀的成员函数展示低层属性和方法,以避免与高级错 误处理成员函数的名称冲突。raw_method_prefix属性用于指定一个不同 的前缀。注意:raw_method_prefix属性的效果不会因raw_method_
36、prefix 属性的存在而改变。在说明一个前缀时,raw_method_prefix总是优先于 raw_interfaces_only若两种属性用在同一个#import语句中时,则采用 raw_method_prefix 指定的前缀。raw_native_types 属性在缺省情况下,高级错误处理方法在BSTR和VARIANT数据类型和原始 COM界面指针的地方使用COM支持类_bctr_t和一variant。这些类封 装了分配和取消分配这些数据类型的存储器存储的细节,并且极大地简 化了类型造型和转换操作。raw_native_types属性在高级wrapper函数中 禁止使用这些COM支持类
37、,并强制替换使用低级数据类型。raw_property_prefix 属性raw_property prefix(GetPrefix,PutPrefix,PutRefPrefix)GetPrefix用于propget方法的前缀PutPrefix用于propput方法的前缀PutRefPrefix用于propputref方法的前缀在缺省情况下,低级方法propget、propput和propputref分别用后缀为 get_ put_和putre的成员函数来展示。这些前缀与MIDL生成的头文 件中的名称是兼容的。raw_property_prefixes属性分别用于说明这三个 属性方法的前缀。r
38、ename属性rename(IdName, NewName)OldName类型库中的旧名NewName用于替换旧名的名称rename属性用于解决名称冲突的问题。若该属性被指定,编译器将在类 型库中的OldName的所有出现处用结果头文件中用户提供的NewName 替换。此属性用于类型库中的一个名称和系统头文件中的宏定义重合时。若这 种情况未被解决,则将产生大量语法错误,如C2059和C2061o 注意:这种替换用于类型库的名称,而不是用于结果头文件中的名称。 这里有一个例子:假设类型库中有一个名称为MyParent的属性,且头文件 中定义了一个用在#import之前的宏GetMyParent由
39、于GetMyParent是 用于错误处理属性get的个wrapper函数的缺省名称,所以将产生一个 名称冲突。为解决这个问题,使用#imprt语句中的以下属性:rename(MyParent,MyParentX)该语句将重新命名类型库中的名称MyParent,而试图重新命名 GetMyParentwrapper 名称将会出错:rename(GetMyParent,GetMyParentX)这是因为名称GetMyParent只出现在结果类型库头文件中。rename_namespace 属性rename_namespace(N ewN ame)NewName名称空间的新名称rename names
40、pace属性用于重新命名包含类型库内容的名称空间。它带 有一个指定名称空间新名newname的参量。消除名称空间可以使用no namespace属性。C+特殊处结束 #include 指令 include指令告诉预处理器处理一个指定文件的内容,就象这些内容以 前就在这条指令出现的源程序中。你可以把常量和宏定义放在包含文件 中,然后用#include指令把这些定义加到任何源文件中。包含文件对于外 部变量和复杂数据类型结合的说明也是有用的。你只需在为此目的创建的个包含文件中定义和命名这些类型次。语法#include path-spec#includepath_spec是个前面有目录说明的任选文件名
41、。这个文件名必须命名 个现存文件。path_spec的语法依赖于编译该程序的操作系统。这两种语法格式都导致用已说明的包含文件的全部内容来替换该指令。 两种格式的区别在于路径未完整指定时预处理器搜索头文件的顺序。语法格式动作引号格式这种格式指示预处理器先在包含#include语句的文件的相同 目录内搜索,然后在任何包括该文件的目录中搜索。随后预处理器沿着7! 编译器选项指定的路径搜索,最后是在!NCLUDE环境变量说明的路径 搜索尖括号格式这种格式指示预处理器首先在/1编译器选项指定的路径中 搜索包含文件。然后在INCLUDE环境变量说明的路径中搜索一旦预处理器找到指定文件,它就立即停止搜索。如果用双引号给出一个 明确完整的包含文件的路径,预处理器将只搜索该路径规格而忽略标准 目录。如果在双引号间的文件名不是个完整的路径规格,预处理器将先搜索 “父”文件的目录。父文件是一个包含#include指令的文件。例如,如果你 把名称为file2的文件包括在个名称为file!的文件中,filel就是父文 件。包含文件可被嵌套;这指的是个#include指令出现在以另个#include 指令命名的文件里。例如,以上的文件file2,可包含文件file3,在这种情况 下,filel是file2的父文件,而且是file3的祖父文件。当包含文件嵌
限制150内