#pragma comment和#pragma 预处理指令详解

该宏放置一个注释到对象文件或者可执行文件。
   
例如,#pragma   comment(lib,"Ws2_32.lib")表示链接Ws2_32.lib这个库。 和在工程设置里写上链入Ws2_32.lib的效果一样,不过这种方法写的 程序别人在使用你的代码的时候就不用再设置工程settings了。

#pragma comment( comment-type [,"commentstring"] )

comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker之一。
commentstring是一个提供为comment-type提供附加信息的字符串,
Remarks:
1、compiler:放置编译器的版本或者名字到一个对象文件,该选项是被linker忽略的。
2、exestr:在以后的版本将被取消。
3、lib:放置一个库搜索记录到对象文件中,这个类型应该是和commentstring(指定你要Liner搜索的lib的名称和路径)
这个库的名字放在Object文件的默认库搜索记录的后面,linker搜索这个这个库就像你在命令行输入这个命令一样。你可以
在一个源文件中设置多个库记录,它们在object文件中的顺序和在源文件中的顺序一样。如果默认库和附加库的次序是需要
区别的,使用Z编译开关是防止默认库放到object模块。
4、linker:指定一个连接选项,这样就不用在命令行输入或者在开发环境中设置了。
只有下面的linker选项能被传给Linker.
  • /DEFAULTLIB

  • /EXPORT

  • /INCLUDE

  • /MANIFESTDEPENDENCY

  • /MERGE

  • /SECTION

(1)/DEFAULTLIB:library

/DEFAULTLIB 选项将一个 library 添加到 LINK 在解析引用时搜索的库列表。用 /DEFAULTLIB
指定的库在命令行上指定的库之后和 .obj 文件中指定的默认库之前被搜索。

忽略所有默认库 (/NODEFAULTLIB) 选项重写 /DEFAULTLIB:library。如果在两者中指定了相同的 library 名称,忽略库 (/NODEFAULTLIB:library) 选项将重写 /DEFAULTLIB:library

(2)/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]

使用该选项,可以从程序导出函数,以便其他程序可以调用该函数。也可以导出数据。通常在 DLL 中定义导出。entryname 是调用程序要使用的函数或数据项的名称。ordinal 在导出表中指定范围在 1 至 65,535 的索引;如果没有指定 ordinal,则 LINK 将分配一个。NONAME 关键字只将函数导出为序号,没有 entryname

DATA 关键字指定导出项为数据项。客户程序中的数据项必须用 extern __declspec(dllimport) 来声明。
有三种导出定义的方法,按照建议的使用顺序依次为:

  1. 源代码中的 __declspec(dllexport)

  2. .def 文件中的 EXPORTS 语句

  3. LINK 命令中的 /EXPORT 规范

所有这三种方法可以用在同一个程序中。LINK 在生成包含导出的程序时还创建导入库,除非生成中使用了 .exp 文件。
LINK 使用标识符的修饰形式。编译器在创建 .obj 文件时修饰标识符。如果 entryname 以其未修饰的形式指定给链接器(与其在源代码中一样),则 LINK 将试图匹配该名称。如果无法找到唯一的匹配名称,则 LINK 发出错误信息。当需要将标识符指定给链接器时,请使用 Dumpbin 工具获取该标识符的修饰名形式。

(3)/INCLUDE:symbol

/INCLUDE 选项通知链接器将指定的符号添加到符号表。

若要指定多个符号,请在符号名称之间键入逗号 (,)、分号 (;) 或空格。在命令行上,对每个符号指定一次 /INCLUDE:symbol
链接器通过将包含符号定义的对象添加到程序来解析 symbol。该功能对于添包含不会链接到程序的库对象非常有用。用该选项指定符号将通过 /OPT:REF 重写该符号的移除。


MSDN描述(http://msdn.microsoft.com/zh-cn/library/aa273890.aspx):
Places a comment record into an object file or executable file. The comment-type is one of five predefined identifiers, described below, that specify the type of comment record. The optional commentstring is a string literal that provides additional information for some comment types. Because commentstring is a string literal, it obeys all the rules for string literals with respect to escape characters, embedded quotation marks ("), and concatenation. 

compiler 

Places the name and version number of the compiler in the object file. This comment record is ignored by the linker. If you supply a commentstring parameter for this record type, the compiler generates a warning. 

exestr 

Places commentstring in the object file. At link time, this string is placed in the executable file. The string is not loaded into memory when the executable file is loaded; however, it can be found with a program that finds printable strings in files. One use for this comment-record type is to embed a version number or similar information in an executable file. 

lib 

Places a library-search record in the object file. This comment type must be accompanied by a commentstring parameter containing the name (and possibly the path) of the library that you want the linker to search. Since the library name precedes the default library-search records in the object file, the linker searches for this library just as if you had named it on the command line. You can place multiple library-search records in the same source file; each record appears in the object file in the same order in which it is encountered in the source file. 

linker 

Places a linker option in the object file. You can use this comment-type to specify a linker option instead placing the option on the Link tab of the Project Settings dialog box. For example, you can specity the /include option to force the inclusion of a symbol: 

#pragma comment(linker, "/include:__mySymbol") 

user 

Places a general comment in the object file. The commentstring parameter contains the text of the comment. This comment record is ignored by the linker. 

The following pragma causes the linker to search for the EMAPI.LIB library while linking. The linker searches first in the current working directory and then in the path specified in the LIB environment variable. 

#pragma comment( lib, "emapi" ) 

The following pragma causes the compiler to place the name and version number of the compiler in the object file: 

#pragma comment( compiler ) 

Note For comments that take a commentstring parameter, you can use a macro in any place where you would use a string literal, provided that the macro expands to a string literal. You can also concatenate any combination of string literals and macros that expand to string literals. For example, the following statement is acceptable: 

#pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ )


=================================================================================
在所有的预处理指令中,#Pragma   指令可能是最复杂的了,它的作用是设定编译器的状态或 
者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与 

C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器 
或操作系统专有的,且对于每个编译器都是不同的。 

其格式一般为:   #Pragma   Para 
其中Para 为参数,下面来看一些常用的参数。 

(1)message 参数。   Message   参数是我最喜欢的一个参数,它能够在编译信息输出窗 
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为: 

#Pragma   message(“消息文本”) 

当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正 
确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自 
己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 

#ifdef   _X86 

#Pragma   message(“_X86   macro   activated!”) 

#endif 

当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_ 
X86   macro   activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 。 

(2)另一个使用得比较多的pragma参数是code_seg。格式如: 
#pragma   code_seg(   [\section-name\[,\section-class\]   ]   ) 
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。 

(3)#pragma   once  (比较常用) 
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6 
中就已经有了,但是考虑到兼容性并没有太多的使用它。 

(4)#pragma   hdrstop
表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预 
编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所 
以使用这个选项排除一些头文件。 

有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#p 
ragma   startup指定编译优先级,如果使用了#pragma   package(smart_init)   ,BCB就会根据 
优先级的大小先后编译。 

 (5)#pragma   resource   \*.dfm\
表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。 

(6)#pragma   warning(   disable   :   4507   34;   once   :   4385;   error   :   164   ) 
等价于: 

#pragma   warning(disable:4507   34)   //   不显示4507和34号警告信息 

#pragma   warning(once:4385)   //   4385号警告信息仅报告一次 

#pragma   warning(error:164)   //   把164号警告信息作为一个错误。 

同时这个pragma   warning   也支持如下格式: 

#pragma   warning(   push   [   ,n   ]   ) 

#pragma   warning(   pop   ) 

这里n代表一个警告等级(1---4)。 

#pragma   warning(   push   )保存所有警告信息的现有的警告状态。 

#pragma   warning(   push,   n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。 

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

#pragma   warning(   push   ) 

#pragma   warning(   disable   :   4705   ) 

#pragma   warning(   disable   :   4706   ) 

#pragma   warning(   disable   :   4707   ) 

//....... 

#pragma   warning(   pop   ) 

在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。 

(7)pragma   comment(...) 
该指令将一个注释记录放入一个对象文件或可执行文件中。 常用的lib关键字,可以帮我们连入一个库文件。 

(8)·通过#pragma   pack(n)改变C编译器的字节对齐方式   
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、 结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺 序存储,第一个成员的地址和整个结构的地址相同。 

           例如,下面的结构各成员空间分配情况: 
struct   test   

           char   x1; 
           short   x2; 
           float   x3; 
           char   x4; 
}; 
           结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填 充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字 节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为 12字节。更改C编译器的缺省字节对齐方式 
           在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件: 
  ·   使用伪指令#pragma   pack   (n),C编译器将按照n个字节对齐。 
           ·   使用伪指令#pragma   pack   (),取消自定义字节对齐方式。 

           另外,还有如下的一种方式: 
           ·   __attribute((aligned   (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。 
           ·   __attribute__   ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。 

以上的n   =   1,   2,   4,   8,   16...   第一种方式较为常见。 

应用实例 

  在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修 改起来也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做, 不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结 构。其协议结构定义如下: 

#pragma   pack(1)   //   按照1字节方式进行对齐 
struct   TCPHEADER   

           short   SrcPort;   //   16位源端口号 
           short   DstPort;   //   16位目的端口号 
           int   SerialNo;   //   32位序列号 
           int   AckNo;   //   32位确认号 
           unsigned   char   HaderLen   :   4;   //   4位首部长度 
           unsigned   char   Reserved1   :   4;   //   保留6位中的4位 
           unsigned   char   Reserved2   :   2;   //   保留6位中的2位 
           unsigned   char   URG   :   1; 
           unsigned   char   ACK   :   1; 
           unsigned   char   PSH   :   1; 
           unsigned   char   RST   :   1; 
           unsigned   char   SYN   :   1; 
           unsigned   char   FIN   :   1; 
           short   WindowSize;   //   16位窗口大小 
           short   TcpChkSum;   //   16位TCP检验和 
           short   UrgentPointer;   //   16位紧急指针 
};   
#pragma   pack()   //   取消1字节对齐方式    
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值