



版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1C语言的预处理程序ANS!标准定义的C语言预处理程序包括下列命令:#definetterrorttinclude#if#else#elifttendif#ifdefSifndefSundef#lineSpragma非常明显,所有预处理命令均以符号#开头,下面分别加以介绍。2#define命令#define定义了一个标识符及ー个串。在源程序中每次遇到该标识符时,均以定义的串代换它。ANSI标准将标识符定义为宏名,将替换过程称为宏替换。命令的一般形式为:#defineidentifierstring注意,该语句没有分号。在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束。例如,如希望TURE取值1,FALSE取值〇,可说明两个宏#define#defineTURE1#defineFALSE0这使得在源程序中每次遇到TURE或FALSE就用0或1代替。例如,在屏幕上打印‘‘012”:printfC%d%d%d",FALSE,TRUE,TRUE+1);宏名定义后,即可成为其它宏名定义中的一部分。例如,下面代码定义了ONE、TWO及THREE的值。SdefineONE1SdefineTWOONE+ONE#defineTHREEONE+TWO懂得宏替换仅仅是以串代替标识符这点很重要。因此,如果希望定义ー个标准错误信息,可编写如下代码:#defineE_MS,zstandarderroroninput\n"printf(E_MS);编译程序遇到标识符E_MS时,就用"standarderroroninput\n”替换。对于编译程序,printfO语句实际是如下形式:printf("standarderroroninput\n;")如果在串中含有标识符,则不进行替换。例如:#defineXYZthisisatestprintf("XYZ");该段不打印"thisisatest"而打印"XYZ"。如果串长于一行,可以在该行末尾用一反斜杠续行,例如:#defineLONG_STRING*thisisaverylong\stringthatisusedasanexampleC语言程序普遍使用大写字母定义标识符。这种约定可使人读程序时很快发现哪里有宏替换。最好是将所有的#define放到文件的开始处或独立的文件中(用#include访问),而不是将它们分散到整个程序中。宏代换的最一般用途是定义常量的名字和程序中的“游戏数”。例如,某ー程序定义了一个数组,而它的儿个子程序要访问该数组,不应直接以常量定数组大小,最好是用名字定义之(需改变数组大小时)。#defineMAX_SIZE100floatbalance[MAXSIZE;]#define命令的另ー个有用特性是,宏名可以取参量。每次遇到宏名时,与之相连的形参均由程序中的实参代替。例如:当编译该程序时,由MlN(a,b)定义的表达式被替换,x和y用作操作数,即printf()语句被代换后取如下形式:printf("theminimumis:%,d"(xくy)?x:y);用宏代换代替实在的函数的一大好处是宏替换增加了代码的速度,因为不存在函数调用的开销。但增加速度也有代价:由于重复编码而增加了程序长度。3#error处理器命令#error强迫编译程序停止编译,主要用于程序调试。4#include命令#include使编译程序将另一源文件嵌入带有#include的源文件,被读入的源文件必须用双引号或尖括号括起来。例如:#include"stdio.h"#includeくstdio.h>这两行代码均使用C编译程序读入并编译用于处理磁盘文件库的子程序。将文件嵌入#include命令中的文件内是可行的,这种方式称为嵌套的嵌入文件,嵌套层次依赖于具体实现。如果显式路径名为文件标识符的一部分,则仅在哪些子目录中搜索被嵌入文件。否则,如果文件名用双引号括起来,则首先检索当前工作目录。如果未发现文件,则在命令行中说明的所有目录中搜索。如果仍未发现文件,则搜索实现时定义的标准目录。如果没有显式路径名旦文件名被尖括号括起来,则首先在编译命令行中的目录内检索。如果文件没找到,则检索标准目录,不检索当前工作目录。5条件编译命令有几个命令可对程序源代码的各部分有选择地进行编译,该过程称为条件编译。商业软件公司广泛应用条件编译来提供和维护某ー程序的许多顾客版本。1.#if>#else,#elif及#endif#if的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif标识ー个#if块的结束,参见例4-130#ifconstant-expressionstatementsequence#endif由于MAX大于99,以上程序在屏幕上显示一串消息。该例说明了一个重点:跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof。#else命令的功能有点象C语言中的else;#else建立另ー选择(在#if失败的情况下)。因而上面的例子可扩充,参见例4T4。在此例中,因为MAX小于99,所以,不编译#if块,而是编译#else块,因此,屏幕上显示‘'compiledforsmallarray•消息。注意,#else既是#if块又是#else块头。这是因为任何#if仅有一个Sendif〇#elif命令意义与ELSEIF相同,它形成一个ifelse-if阶梯状语句,可进行多种编译选择。#elif后跟ー个常量表达式。如果表达式为true,则编译其后的代码块,不对其它#elif表达式进行测迖。否则,顺序测试下ー块。#ifexpressionstatementsequence#elifexpressionlstatementsequence#elifexpression2statementsequence#elifexpressionsstatementsequenceelifexpression4elifexpression3Nstatementsequence#endif例如:下面程序利用ACTIVe_COUNTRY定义货币符号。defineUS0defineENGLAND1defineFRANCE2defineACTIVE_COUNTRYUSifACTIVE_COUNTRY==UScharcurrency[]-dollar;elifACTIVE_COUNTRY==ENGLANDcharcurrency[]="pound;"#elsecharcurrency[]=*franc;"Sendifif与#elif命令可能一直嵌套到实现规定的权限,其中#endif、#else或#elif与最近#if或#elif关联。例如,下面程序是完全有效的。#ifMAX>100#ifSERIAL_VERSIONintport=198;#elifintport=200;#elif#elsecharoutbuffer[100];#endif2.#ifdef和#ifndef条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示“如果有定义”及“如果无定义”。ifdef的一•般形式是:ifdefmacronamestatementsequencettendif如果宏名在前面#define语句中已定义过,则该语句后的代码块被编译。ifndef的一般形式是:#ifndefmacronamestatementsequencettendif如果宏名在#define语句中无定义,则编译该代码块。ttifde!与#ifndef可以用于#else语句中,但#elif不行。参见4-15〇上述代码打印“HiTed”及“RALPHnotdefinedw〇如果TED没有定义,则显示“Hiany〇ne",后面是"RALPHnotdefinedM〇可以像嵌套#if那样将#ifdef与#ifndef嵌套至任意深度。#undef命令#undef取消其后那个前面已定义过有宏名定义。一般形式为:#undefmacroname例如:defineLEN100difineWIDTH100chararray[LEN][WIDTH];undefLENundefWIDTH*atthispointbothLENandWIDTHareundefined*/直到遇到#undef语句之前,LEN与WIDTH均有定义。undef的主要目的是将宏名局限在仅需要它们的代码段中。#Iine命令#line改变_LINE一与一FILE一的内容,它们是在编译程序中预先定义的标识符。命令的基本形式如下:#linenumber[/zfi1ename"]其中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源程序中当前行号,文件名为源文件的名字。命令#line主要用于调试及其它特殊应用。例如,下面说明行计数从1〇〇开始;printf()语句显示数102I因为它是语句#line100后的第3行。#line100/・初始化行计数器*/main()/*行号100*/{/・行号101*/printf(/z%d\n/z,_line_);/・行号102*/)Spragma命令#pragma为实现时定义的命令,它允许向编译程序传送各种指令。例如,编译程序可能有一ー种选择,它支持对程序执行的跟踪。可用#pragma语句指定一个跟踪选择。在所有的预处理指令中,Spragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和生语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:Spragmapara其中para为参数,下面来看•些常用的参数。(1)message参数message参数是我最喜欢的ー个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:#pragmamessage("消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检査。假设我们希望判断自己有没有在源代码的什么地方定义了一X86这个宏,可以用下面的方法:#ifdef_X86ttpragmamessage(ZZ_X86macroactivated!zz)#endif我们定义了一X86这个宏以后,应用程序在编译时就会在编译输出窗口里显不86macroactivated!zzo我们就不会因为不记得自己定义的ー些特定的宏而抓耳挠腮了。(2)另--个使用得比较多的pragma参数是code_seg格式如:Spragmacode_seg(["section-name”[,"section-class"]])它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。(3)#pragmaonce仕匕较常用)只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。⑷#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB就会根据优先级的大小先后编译。(5)#pragmaresource dfm"表示把・.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。(6)ftpragmawarning(disable:450734;once:4385;error:164)等价于:#pragmawarning(disable:450734) //不显示4507和34号警告信息Spragmawarning(once:4385) //4385号警告信息仅报告ー次ttpragmawarning(error:164) 〃把!64号警告信息作为一1个错误。同时这个pragmawarning也支持如下格式:#pragmawarning(push[,n])Spragmawarning(pop)这里n代表ー个警告等级(1一一4)〇pragmawarning(push)保存所有警告信息的现有的警告状态。Spragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。Spragmawarning(pop)向栈中弹出最后ー个警告信息,在入栈和出栈之间所作的一切改动取消。例如:pragmawarning(push)pragmawarning(disable:4705)ttpragmawarning(disable:4706)Spragmawarning(disable:4707)// Spragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。(7)#pragmacomment(...)该指令将一个注释记录放入ー个对象文件或可执行文件中。常用的lib关键字,可以帮我们连入ー个库文件。如:ttpragmacomment(lib,"comctl32.lib)pragmacomment(lib,〃vfw32.lib")ttpragmacomment(lib,"wsock32,lib)每个编译程序可以用#pragma指令激活或终止该编译程序支持的ー些编译功能。例如,对循环优化功能:Spragmaloop_opt(on) //激活ttpragmaloop_opt(off) /Z终止有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如aParameterxxxisneverusedinfunctionxxx”,可以这样:ttpragmawarn-100 //Turnoffthewarningmessageforwarning#100intinsert_record(REC*r){/*functionbody*/)pragmawarn+100 //Turnthewarningmessageforwarning#100backon函数会产生一条有唯一特征码!00的警告信息,如此可暂时终止该警告。每个编译器对#pragma的实现不同,在ー个编译器中有效在别的编译器中儿乎无效。可从编译器的文档中查看。补充 #pragmapack与内存对齐问题许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignmentmodulus)〇Win32平台下的微软C编译器(cl.exefor80x86)在默认情况下采用如下的对齐规则:任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)o比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(!字节)则可以从任何ー个地址开始。Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):任何2字节大小(包括单字节吗?)的数据类型仕匕如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。ANSIC规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。那么结构体本身有什么对齐要求吗?有的,ANSIC标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。如何使用C/C++中的对齐选项vc6中的编译选项有/Zp[l]2|4|8|16],/Zpl表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。n字节边界对齐的意思是说,ー个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。也就是:min(sizeof(member),n)实际上,I字节边界对齐也就表示了结构成员之间没有空洞。/Zpn选项是应用于整个工程的,影响所有的参与编译的结构。要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择CodeGeneration分类,在Structmemberalignment可以选择。要专门针对某些结构定义使用对齐选项,可以使用#pragmapack编译指令:Spragmapack([n])该指令指定结构和联合成员的紧凑对齐。而ー个完整的转换单元的结构和联合的紧凑对齐由/Zp选项设置。紧凑对齐用pack编译指示在数据说明层设置。该编译指示在其出现后的第ー个结构或联合说明处生效。该编译指示对定义无效。当你使用#pragmapack(n)时,这里n为1、2、4、8或16〇第一个结构成员之后的每个结构成员都被在値在更小的成员类型或n字节界限内。如果你使用无参量的#pragmapack,结构成员被紧凑为以/Zp指定的值。该缺省/Zp紧凑值为/Zp8。(2)编译器也支持以下增强型语法:#pragmapack([[{push|pop},][identifier,]][n])若不同的组件使用pack编译指示指定不同的紧凑对齐,这个语法允许你把程序组件组合为ー个单独的转换单元。带push参量的pack编译指示的每次出现将当前的紧凑对齐立磕到ー个内部编译器堆栈中。编译指示的参量表从左到右读取。如果你使用push,则当前紧凑值被存储起来;如果你给出ー个n的值,该值将成为新的紧凑值。若你指定一个标识符,即你选定一个名称,则该标识符将和这个新的的紧凑值联系起来。带ー个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。如果你使用pop参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值,并且将产生一个警告信息。若你使用pop且指定一个n的值,该值将成为新的紧凑值。若你使用p〇p且指定一个标识符,所有存储在堆栈中的值将从栈中删除,直到找到ー个匹配的标识符,这个与标识符相关的紧凑值也从栈中移出,并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符,将使用命令行设置的紧凑值,并且将产生一个一级警告。缺省紧凑对齐为8。pack编译指示的新的增强功能让你编写头文件,确保在遇到该头文件的前后的紧凑值是一样的。(3)栈内存对齐在vc6中栈的对齐方式不受结构成员对齐选项的影响。它总是保持对齐,而且对齐在4字节边界上。9预定义的宏名ANSI标准说明了五个预定义的宏名。它们是:_line__FILE__DATE__TIME__STDC_如果编译不是标准的,则可能仅支持以上宏名中的儿个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。line_及ーFILE一宏指令在有关#line的部分中已讨论,这里讨论其余的宏名。DATE一宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。源代码翻译到目标代码的时间作为串包含在一TIME_中。串形式为时:分:秒。如果实现是标准的,则宏一sTDC―含有十进制常量lo如果它含有任何其它数,则实现是非标准的。注意:宏名的书写由标识符与两边各二条下划线构成。10注释在C语言中,所有的注释由字符/・开始,以・/结束。在星号及斜杠之间不允许有空格。编译程序忽略注释开始符到注释结束符间的任何文本。例如,下面程序在屏幕上只打印"hell〇”〇main()(printf("hello");/*printf("Thisisasampletoprinthell;0"*/)}注释可出现在程序的任何位置,但它不能出现在关键字或标识符中间。即,注释x=10+/*addthenumbers*/5:是有效的,但swi/*thiswillnotwork*/tch(c){...是不正确的,因为C的关键字不能含有注释。通常也不希望表达式中间出现注释,因为这会使意义含混不清。注释不可嵌套,即ー个注释内不可含有另ー个注释。例如,下面代码段在编译时出错:/♦thisisanoutercommentx=y/a;/*thisisaninnercomment-andcausesanerror*//当需要解释程序的行为时,注释应简明扼要。除了最简单和最直观的函数外,都应有注释,在函数开始处说明其功能,如何调用以及返回何处pragma预处理指令作用说明在所有的预处理指令中,#pragma指令可能算是比较复杂的一条,它的作用是设定编译器的状态或者是指示编译器完成•些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:#pragmapara:其中para为参数,下面我们就一些常用的参数作一•些说明。message参数:message参数主要用于在编译信息输出窗口中输出相应的信息;当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来;这对于源代码信息的控制是非常重要的;其格式如下:#pragmamessage("消息文本”)当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了一X86这个宏可以用下面的方法:#ifdef_X86#pragmamessage("_X86macroactivated!w)#endif当我们定义了一X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示"_X86macroactivated!v。我们就不会因为不记得自己定义的ー些特定的宏市抓耳挠腮了。code_seg参数:此参数主要用于设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。其格式如下:#pragmacode_seg([[{pushpop),][identifier,」」Lsegment-name”[,"segment-class"]])该指令用来指定函数在•obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在•obj文件中默认的存放节为.text节,如果code_seg没有带参数的话,则函数存放在.text节中;push(可选参数)将一个记录版到内部编译器的堆栈中;可选参数可以为…个标识符或者节名;pop(可选参数)将一个记录从堆栈顶端弹出,该记录可以为ー个标识符或者节名;identifier(可选参数)当使用push指令时,为压入堆栈的记录指派的ー个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈;"segment-name”(可选参数)表示函数存放的节名。例如:〃默认情况下,函数被存放在.text节中voidfund(){//storedin.text)〃将函数存放在.my_datal节中#pragmacode_seg(".my_datal")voidfunc2(){//storedinmydatal)//rl为标识符,将函数放入.my_data2节中#pragmacode_seg(push,rl,".my_data2")voidfunc3(){ //storedinmy_data2}intmain(){)once参数:这是ー个比较常用的能数,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次;其用法如下:#pragmaoncehdrstop参数:hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译;BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除ー些头文件;有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译;你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart一init),BCB就会根据优先级的大小先后编译;其用法如下:#pragmahdrstop
5.warning5.warning参数:该指令允许有选择性的修改编译器的警告消息的行为;指令格式如下:ttpragmawarning(warning-spec:warning-number-list[;warning-spec:warning-number-list...])#pragmawarning(push[,n])#pragmawarning(pop)主要用到的警告表示有如下几个:once:只显示一次(警告/错误等)消息default:重置编译器的警告行为到默认状态1,2,3,4:四个警告级别disable:禁止指定的警告信息error:将指定的警告信息作为错误报告如果大家对上面的解释不是很理解,可以参考一下下面的例子及说明:#pragmawarning(disable:450734;once:4385;error:164)这条指令等价于如下指令:ttpragmawarning(disable:450734)//不显示4507和34号警告信息#pragmawarning(once:4385) //4385号警告信息仅报告一次ttpragmawarning(error:164)/Z把164号警告信息作为ー个错误。同时这个pragmawarning也支持如下格式:#pragmawarning(push[,n])ttpragmawarning(pop)这里n代表一个警告等级(1一一4)〇ttpragmawarning(push)保存所有警告信息的现有的警告状态。#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。#pragmawarning(pop)向栈中弹出最后一,个警告信息,在入栈和出栈之间所作的一切改动取消。例如:#pragmawarning(push)pragmawarning(disable:4705)pragmawarning(disable:4706)pragmawarning(disable:4707)pragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707);在使用标准C++进行编程的时候经常会得到很多的警告信息,而这些警告信息都是不必要的提示,所以我们可以使用#pragmawarning(disable:4786)来禁止该类型的警告;在vc中使用AD0的时候也会得到不必要的警告信息,这个时候我们可以通过#pragmawarning(disable:4146)来消除该类型的警告信息。comment参数:该参数将一个注释记录放入ー个对象文件或可执行文件中;格式如下:#pragmacomment(comment-type[,commentstring])comment-type(注释类型):可以指定为五种预定义的标识符的其中一种五种预定义的标识符如下:0 compiler:将编译器的版本号和名称放入目标文件中,本条注释记录将被编译器忽略如果你为该记录类型提供了commentstring参数,编译器将会产生一个警告。例如:ftpragmacomment(compiler)0 exestr将commentstring参数放入目标文件中,在链接的时候这个字符串将被放入到可执行文件中,当操作系统加载可执行文件的时候,该参数字符串不会被加载到内存中.但是,该字符串可以被dumpbin之类的程序查找出并打印出来,你可以用这个标识符将版本号码之类的信息嵌入到可执行文件中!0 lib这是一个非常常用的关键字,用来将一个库文件链接到目标文件中常用的lib关键字,可以帮我们连入ー个库文件。例如:#pragmacomment(lib,“user32.lib")该指令用来将user32.1ib库文件加入到本工程中。0 linker将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:ttpragmacomment(linker,"/include: mySymbol")你可以在程序中设置下列链接选项:/DEFAULTLIB/EXPORT/INCLUDE/MERGE/SECTION这些选项在这里就不一,ー说明了,详细信息请看msdn!0 user将一般的注释信息放入目标文件中commentstring参数包含注释的文本信息,这个注释记录将被链接器忽略。例如:#pragmacomment(user,"Compiledon"_DATE"at"_TIME)pack参数:此参数主要用于设置结构体中变量的对齐方式:如果设置的值比结构体中字节最长的类型还要大,则这个变量(注意仅针对这ー个变量)只按照它的字节长度对齐,即不会出现内存浪费的情况;其对齐有1、2、4、8、16,其中默认值为8;其用格式如下:#pargmapack(n)例子例如:〃每个变量按照1字节对齐Wpragmapack(1)structA〃每个变量按照1字节对齐inty; //alignedonbyteboundary1);sizeof(A)==5^pragmapack(2) 〃每个变量按照2字节对齐structA(charx; //alignedonbyteboundary0inty; //alignedonbyteboundary2};sizeof(A)==6#pragmapack(4) 〃每个变量按照4字节对齐structA(charx; //alignedonbyteboundary0inty; //alignedonbyteboundary4};sizeof(A)ニニ8Spragmapack() 〃默认,相当于#pragmapack(8)每个变量按照8字节对齐structAcharx; //alignedonbyteboundary0inty; //alignedonbyteboundary4};sizeof(A)==8但是这里y的大小是4字节,所以不会按照8字节对齐,否则将造成1个int空间的浪费:在本人的工作当中这条指令主要用于网络数据包传输中用的比较多,毕竟现在的计算机上内存都比较大,所以浪费一点空间也是可以忍受的,更何况浪费的同时能给我们的处理速度带来一定的提高刚接触到MFC编程的人往往会被MFC向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的ー个强大工具。使用它们可以进行简单的源代码控制,版本控制,预警或者完成•些特殊的功能。ー个经典的例子使用预处理与宏定义最经典的例子莫过于加在一个头文件中以避免头文件被两次编译。试想这种的情况,有一ー个文件headerfile,h它被包含在headerfilel.h中,同时在headerfile2.h中也被包含了,现在有一个CPP文件,implement,cpp包含了headerfilel.h和headerfile2.h:#includeKheaderfilel.hw#include"headerfile2.h”假设headerfile.h中定义了一个全局变量iglobal〇intiglobal;在编译的时候编译器两次编译headerfile,也就会发现iglobal被定义了两次,这时就会发生变量重定义的编译错误。传统的解决办法是使用#ifdef以及#endif来避免头文件的重复编译,在上面的例子中,只需要加上这么几行:#ifndefsmartnose_2002_6_2Iheaderfi1eh#definesmartnose_2002_6_2l_headerfile_hintiglobal;#endif仔细的考虑上面的宏定义,会发现当编译器编译过一次headerfile.h以后,smartnose_2002_6_21_headerfile_h这个宏就被定义了,以后对headerfile,h的编译都会跳过intiglobal这一行。当然smartnose_2002_6_21_headerfile_h这个宏是可以任意定义的,但是这个宏本身不能和其它文件中見义的宏重复,由以MFC在自动生成的文件中总是使用一个随机产生的长度非常长的宏,但我觉得这没有必要,我建议在这个宏中加入ー些有意义的信息,比方作者,文件名,文件创建时间等等,因为我们有时候会忘记在注释中加入这些信息。在VC.Net中我们不会再看见这些宏定义了,因为在这里会普遍使用ー个预处理指令:#pragmaonce只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。源代码版本控制当我们为许多平台开发多个版本的时候预编译指令和宏定义也能够帮我们的忙。假设我们现在为WINDOWS和LINUX开发了一套软件,由于这两种系统的不同,我们不得不在程序控制源代码的版本。比方内存的分配,我们可以在LINUX上使用标准C的malloc函数,但是我们希望在WINDOWS上使用HeapAllocAPI〇下面的代码演示了这种情况:main()#ifdef_WINDOWS_PLATFORMHeapAlloc(5);#elsemalloc(5);Sendif当我们在WINDOWS平台上编译此程序的时候,只需要定义WINDOWS-PLATFORM这个宏,那么HeapAlloc这条语句就能够起作用了。这样就能够让我们在同一个文件中为不同的平台实现不同版本的代码,同时保持程序的良好结构。在许多情况下,我们还可以为ー个方法使用不同的算法,然后用宏定义来针对不同的情况选择其中的ー个进行编译。这在MFC应用程序中是使用得最多的。最明显的就是文件中经常存在的#ifdef_DEBUG some code Sendif这样的代码,这些代码在应用程序的调试版(DEBUG)中会发挥其作用。SPragma指令在所有的预处理指令中,SPragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。其格式一般为SPragmaPara其中Para为参数,下面来看一些常用的参数。message参数。Message参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:一•message参数。message它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:Spragmamessage("消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检査。假设我们希望判断自己有没有在源代码的什么地方定义了一X86这个宏可以用下面的方法#ifdef_X86^pragmamessage("_X86macroactivated!")Sendif当我们定义了一X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示X86macroactivated!"〇我们就不会因为不记得自己定义的ー些特定的宏而抓耳挠腮了二.另一个使用得比较多的#pragma参数是code_seg。格式如:Spragmacode_seg([[{push|pop},][identifier,]][segment-name[,"segment-class*])该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在.obj文件中默认的存放节为.text节如果code_seg没有带参数的话,则函数存放在.text节中push(可施参数)将一个记录放到内部编译器的堆栈中,可选参数可以为ー个标识符或者节名pop(可选参数)将一个记录从堆栈顶端弹出,该记录可以为ー个标识符或者节名identifier(可选参数)当使用push指令时,为压入堆栈的记录指派的ー个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈"segment-name”(可选参数)表示函数存放的节名例如:〃默认情况下,函数被存放在.text节中voidfund(){//storedin.text〃将函数存放在.my_datal节中Spragmacode_seg(".my_datal")voidfunc2(){//storedinmy_datal}//rl为标识符,将函数放入.my_data2节中#pragmacode_seg(push,rl,.my_data2")voidfunc3(){//storedinmy_data2)intmain(){)三.Spragmaonce仕匕较常用)这是ー个比较常用的指令,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次四.#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB就会根据优先级的大小先后编译。五.#pragmawarning指令该指令允许有选择性的修改编译器的警告消息的行为指令格式如下:^pragmawarning(warning-specifier:warning-number-list[;warning-specifier:warning-number-list...]#pragmawarning(push[,n])#pragmawarning(pop)主要用到的警告表示有如下几个:once:只显示一次(警告/错误等)消息default:重置编译器的警告行为到默认状态1,2,3,4:四个警告级别disable:禁止指定的警告信息error:将指定的警告信息作为错误报告如果大家对上面的解释不是很理解,可以参考ー•下下面的例子及说明Spragmawarning(disable:450734;once:4385;error:164)等价于:#pragmawarning(disable:450734)//不显示4507和34号警告信息Spragmawarning(once:4385)//4385号警告信息仅报告一次Spragmawarning(error:164)/Z把164号警告信息作为ー个错误。同时这个pragmawarning也支持如下格式:#pragmawarning(push[,n])#pragmawarning(pop)这里n代表ー个警告等级(1—4)〇Spragmawarning(push)保存所有警告信息的现有的警告状态。#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。ttpragmawarning(pop)向栈中弹出最后ー个警告信息,在入栈和出栈之间所作的一切改动取消。例如:Spragmawarning(push)#pragmawarning(disable:4705)#pragmawarning(disable:4706)Spragmawarning(disable:4707)Spragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)在使用标准C++进行编程的时候经常会得到很多的警告信息,面这些警告信息都是不必要的提示,所以我们可以使用#pragmawarning(disable:4786)来禁止该类型的警告在vc中使用ADO的时候也会得到不必要的警告信息,这个时候我们可以通过Spragmawarning(disable:4146)来消除该类型的警告信息六•pragmacomment(...)该指令的格式为#pragmacomment("comment-type*[,commentstring])该指令将一个注释记录放入ー个对象文件或可执行文件中,comment-type(注释类型):可以指定为五种预定义的标识符的其中一种五种预定义的标识符为:compiler:将编译器的版本号和名称放入目标文件中,本条注释记录将被编译器忽略如果你为该记录类型提供了commentstring参数,编译器将会产生・■一个警告例如:#pragmacomment(compiler)exestr:将commentstring参数放入目标文件中,在链接的时候这个字符串将被放入到可执行文件中,当操作系统加载可执行文件的时候,该参数字符串不会被加载到内存中.但是,该字符串可以被dumpbin之类的程序査找出并打印出来,你可以用这个标识符将版本号码之类的信息嵌入到可执行文件中!1ib:这是ー个非常常用的关键字,用来将一个库文件链接到目标文件中常用的lib关键字,可以帮我们连入ー个库文件。例如:#pragmacomment(lib,“user32.lib")该指令用来将user32.lib库文件加入到本工程中linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:#pragmacomment(linker,"/include:_mySymbol")你可以在程序中设置下列链接选项/DEFAULTLIB/EXPORT/INCLUDE/MERGE/SECTION这些选项在这里就不ー,ー说明了,详细信息请看msdn!user:将一般的注释信息放入目标文件中commentstring参数包含注释的文本信息,这个注释记录将被链接器忽略例如:Spragmacomment(user,"Compiledon"DATE *at* TIME)补充一个Spragmapack(n)控制对齐如Spragmapack(push)ttpragmapack(1)structs_l{charszname[l];inta;);#pragmapack(pop)structs_2{charszname[l];inta;);则printf(,zs_lsize:%d\n〃,sizeof(structs_l));printf(/zs_2size:%d\n〃,sizeof(structs_2));得到5,8〇#pragma的用法#pragma是ー个C语言中的预处理指令,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:ttpragmaPara其中Para为参数,下面来看ー些常用的参数。(l)message参数。Message参数是我最喜欢的ー个参数,它能够在编译信息输出]窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:SPragmamessage("消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检査。假设我们希望判断自己有没有在源代码的什么地方定义了一X86这个宏可以用下面的方法#ifdef_X86SPragmamessage("_X86macroactivated!")ftendif当我们定义了一X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示X86macroactivated!"〇我们就不会因为不记得自己定义的ー些特定的宏而抓耳挠腮了(2)另•个使用得比较多的pragma参数是code_seg0格式如:Spragmacode_seg([“section-name“しsection-class"]])它能够设置程ホ中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。(3)#pragmaonce(比较常用)只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。(4)#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除ー些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB就会根据优先级的大小先后编译。(5)ftpragmaresource dfm”表示把・.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。(6)#pragmawarning(disable:450734;once:4385;error:164)等价于:Spragmawarning(disable:450734)//不显示4507和34号警告信息Spragmawarning(once:4385)//4385号警告信息仅报告ー一次Spragmawarning(error:164)/Z把164号警告信息作为ー个错误。同时这个pragmawarning也支持如下格式:Spragmawarning(push[,n])Spragmawarning(pop)这里n代表ー个警告等级(1一一4)。#pragmawarning(push)保存所有警告信息的现有的警告状态。#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。#pragmawarning(pop)向栈中弹出最后ー个警告信息,在入栈和出栈之间所作的一切改动取消。例如:#pragmawarning(push)#pragmawarning(disable:4705)Spragmawarning(disable:4706)Spragmawarning(disable:4707)// #pragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。pragmacomment(...)该指令将一个注释记录放入ー个对象文件或可执行文件中。常用的lib关键字,可以帮我们连入ー个库文件。progmapack(n)指定结构体对齐方式!Spragmapack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。下面举例说明其用法。Spragmapack(push)〃保存对齐状态Spragmapack(4)〃设定为4字节对齐structtest(charml;doublem4;intm3;};Spragmapack(pop)〃恢复对齐状态为测试该功能,可以使用sizeof()测试结构体的长度!VC预定义的宏在VC中有一类宏并不是由用户用#define语句定义的,而是编译器本身就能够识别它们。这些宏的作用也是相当大的。让我们来看第一个,也是MFC中使用得最频繁的ー个:_FILE_〇当编译器遇到这个宏时就把它展开成当前被编译文件的文件名。好了,我们马上就可以想到可以用它来做什么,当应用程序发生错误时,我们可以报告这个错误发生的程序代码在哪个文件里,比方在文件test,cpp中有这样的代码:try{'char*p=new(char[10]);)catch(CException*e)(TRACE(athereisanerrorinfile:%s\n”,_FILE );在程序运行的时候,如果内存分配出现了错误,那么在调试窗口中会出现thereisanerrorinfile:test,cpp这句话,当然,我们还可以把这个错误信息显示在别的地方。如果我们还能够记录错误发生在哪一行就好了,幸运的是,与_FILE_宏定义ー样,还有一个宏记录了当前代码所在的行数,这个宏是_LINE_。使用上面的两个宏,我们可以写出ー个类似于VC提供的ASSERT语句。下面是方法#defineMyAssert(x)\if(!(x))\MessageBox(_FILE_,_LINE_,NULL,MB_OK);我们在应用程序中可以象使用ASSERT语句ー样使用它,在错误发生时,它会弹出ー个对话框,其标题和内容告诉了我们错误发生的文件和代码行号,方便我们的调试,这对于不能使用ASSERT语句的项目来说是非常有用的。除了这两个宏以外,还有记录编译时间的_TIME_,记录日期的_DATE_,以及记录文件修改时间的ーTIMESTAMP_宏。使用这些预定义的宏,我们儿乎可以生成和VC能够生成的一样完整的源代码信息报表。结论翻开MFC和Linux的源代码,宏定义几乎占据了半边天,消息映射,队列操作,平台移植,版本管理,甚至内核模块的拆卸安装都用宏定义完成。毫不夸张的说,有些文件甚至就只能看见宏定义。所以学习宏定义,熟练的使用宏定义对于学习C语言乃至VC都是非常关键的。在上一篇文章中,我演示了儿个常用的宏定义和预处理指令,但可以说这些都是相当常规的技巧。下面要介绍的宏定义与预处理指令的用法也是ATL,MFC以及LINUX中使用得比较多的非常重要的技巧。##连接符与#符##连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#符是把传递过来的参数当成字符串进行替代。下面来看看它们是怎样工作的。这是MSDN上的ー个例子。假设程序中已经定义了这样ー个带参数的宏:Sdefinepaster(n)printf("token"#n"=%d",token##n)同时又定义了一个整形变量:inttoken9=9;现在在主程序中以下面的方式调用这个宏:paster(9);那么在编译时,上面的这句话被扩展为:printf("token""9""=%d",token9);注意到在这个例子中,paster(9);中的这个"9"被原封不动的当成了一个字符串,与“token”连接在了一起,从而成为了token9o而#n也被“9”所替代。可想而知,上面程序运行的结果就是在屏幕上打印出token9=9在ATL的编程中,我们查看它的源代码就会经常看见这样的一段:#defineIMPLEMENTS_INTERFACE(Itf)\{&IID_##Itf,ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls,Itf)},我们经常不假思索的这样使用它:IMPLEMENTS_INTERFACE(ICat)实际上IID」Cat已经在别的地方由ATL向导定义了。当没有向导的时候,你只要遵循把IIDJJロ在你的接口名前面来定义GUID的规则就也可以使用这个宏。在实际的开发过程中可能很少用到这种技巧,但是ATL使用得如此广泛,而其中又出现了不少这样的源代码,所以明白它是怎么一回事也是相当重要的。我的・个朋友就是因为不知道IMPLEMENTSJNTERFACE宏是怎么定义的,而又不小心改动了!ID_ICat的定义而忙活了一整天。Linux的怪圈在刚开始阅读Linux的时候有一个小小的宏让我百思不得其解:#definewaitevent(wq,condition)\do{\if(condition)\break;\_wait_event(wq,condition);\}while(0)这是一个奇怪的循环,它根本就只会运行一次,为什么不去掉外面的do{..}while结构呢?我曾一度在心里把它叫做“怪圈”。原来这也是非常巧妙的技巧。在工程中可能经常会引起麻烦,而上面的定义能够保证这些麻烦不会出现。下面是解释:假设有这样一个宏定义Sdefinemacro(condition)\if(condition)dosomethingO;现在在程序中这样使用这个宏:if(temp)macro(i);elsedoanotherthing();一切看起来很正常,但是仔细想想。这个宏会展开成:if(temp)if(condition)dosomethingO;elsedoanotherthing();这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。为了避免这个错误,我们使用do{….}while(0)把它包裹起来,成为…个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(O)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。几个小小的警告正如微软声称的ー样,宏定义与预编译器指令是强大的,但是它又使得程序难以调试。所以在定义宏的时候不要节省你的字符串,一定要力争完整的描述这个宏的功能。同时在定义宏的时候如有必要(比方使用了if语句)就要使用do{…}while(O)将它封闭起来。在宏定义的时候一定要注意各个宏之间的相互依赖关系,尽量避免这种依赖关系的存在。下面就有这样•个例子。设有一个静态数组组成的整型队列,在定义中使用了这样的方法:intarray[]={5,6,7,8};我们还需要在程序中遍历这个数组。通常的做法是使用•个宏定义#defineELE一NUM4for(intI=O;KELE_NUM;I++)(coutくくarray[I];由于某种偶然的原因,我们删除了定义中的ー个元素,使它变成:array[]={5,6,7}而却忘了修改ELE_NUM的值。那么在上面的代码中马上就会发生访问异常,程序崩溃。然后是彻夜ネ眠的调试,最后发现问题出在这个宏定义上。解决这个问题的方法是不使用array[]={….}这样的定义,而显式的申明数组的大小:array[ELENUM]={•,,.}这样在改动数组定义的时候,我们就不会不记得去改宏定义了。总之,就是在使用宏定义的时候能够用宏定义的地方统统都用上。我发现的另ー个有趣的现象是这样的:假设现在有一个课程管理系统,学生的人数用宏定义为:SdefineSTU_NUM50而老师的人数恰好也是50人,于是很多人把所有涉及到老师人数的地方通通用上STLNUM这个宏。另ー个学期过去,学生中的一个被开除了,系统需要改变。怎么办呢?简单的使用#defineSTU_NUM49么?如果是这样,ー个老师也就被开除了,我们不得不手エ在程序中去找那些STU_NUM宏然后判断它是否是表示学生的数目,如果是,就把它改成49。天哪,这个宏定义制造的麻烦比使用它带来的方便还多。正确的方法应该是为老师的数目另外定义ー个宏:^defineTEANUM50当学生的数目改变以后只要把STU_NUM定义为49就完成了系统的更改。所以,当程序中的两个量之间没有必然联系的时候一定不要用其中的ー个宏去替代另ー个,那只会让你的程序根本无法改动。最后,建议C/C++语言的初学者尽可能多的在你的程序中使用宏定义和预编译指令。多看看MFC,ATL或者LINUX的源代码,你会发现C语言强大的原因所在。在#Pragma是预处理指令它的作用是设定编译器的状态或者是指示编译器完成ー些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式ー一般为:^PragmaPara其中Para为参数,下面来看一些常用的参数。(1)message参数。Message参数是我最喜欢的・,个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:#Pragmamessage("消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了一X86这个宏可以用下面的方法#ifdef_X86^Pragmamessage("_X86macroactivated!")#endif当我们定义了一X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示X86macroactivated!"〇我们就不会因为不记得自己定义的ー些特定的宏而抓耳挠腮了。(2)另ー个使用得比较多的pragma参数是code_sego格式如:#pragmacode_seg([\section-name\[,ヽsection-classヽ[])它能够设置程序中函数代码存放的代码段,使用没有section-name字符串的#pragmacode_seg可在编译开始时将其复位,当我们开发驱动程序的时候就会使用到它。(3)#pragmaonce(比较常用)只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。(4)#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#Pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB就会根据优先级的大小先后编译。(5)#pragmaresource\*.dfmゝ表示把・.dfm文件中的资源加入工程。・.dfm中包括窗体外观的定义。#pragmawarning(disable:450734;once:4385;error:164)等价于:Spragmawarning(disable:450734)//不显示4507和34号警告信息#pragmawarning(once:4385)//4385号警告信息仅报告一次Spragmawarning(error:164)/Z把164号警告信息作为ー个错误。同时这个pragmawarning也支持如下格式:#pragmawarning(push[,n])这里n代表ー个警告等级(1—4)〇#pragmawarning(push)保存所有警告信息的现有的警告状态。#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。#pragmawarning(pop)向栈中弹出最后ー个警告信息,在入栈和出栈之间所作的一切改动取消。例如:#pragmawarning(push)Spragmawarning(disable:4705)#pragmawarning(disable:4706)#pragmawarning(disable:4707)// #pragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。pragmacomment(...)该指令将一个注释记录放入ー个对象文件或可执行文件中。常用的lib关键字,可以帮我们连入ー个库文件。•通过#pragmapack(n)改变C编译器的字节对齐方式在C语言中,结构是ー种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是ー些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。例如,下面的结构各成员空间分配情况:structtest(charxl;shortx2;floatx3;charx4;);结构的第一个成员xl,其偏移地址为〇,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和xl之间填充了ー个空字节。结构的第三个成员
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度建筑工程安全生产责任追究合同
- 2025年度外贸合同书样本:国际货物运输保险合同
- 2025年度商业地产产权转让与物业管理合同
- 2025年度园林绿化养护临时用工合作协议
- 二零二五年度移动宽带网络用户满意度提升合同
- 工业园区升级补贴合同
- 2025年度建筑工程合同监理实施办法
- 2025年度商场顾客满意度调查与提升合同
- 2025年度房屋租赁安全免责合同(带宠物)
- 2025年导电银浆行业现状分析:导电银浆市场复合年增长率为20.12%
- 标准 DB37T 3690.1-2019 液体菌种制备技术规程 第1部分:香菇规范
- 2021五年级道德与法治培优辅差计划3篇
- 静脉药物配置中心课件
- DB11T 852-2019 有限空间作业安全技术规范
- 最新2022年减肥食品市场现状与发展趋势预测
- 材料化学合成与制备技术
- 发展汉语初级综合1:第30课PPT课件[通用]
- 马工程西方经济学(第二版)教学课件-(4)
- 医疗废物管理组织机构架构图
- cjj/t135-2009《透水水泥混凝土路面技术规程》
- 社保人事专员绩效考核表
评论
0/150
提交评论