pragma预处理指令略解

来源:互联网 发布:nokia-e63软件下载 编辑:程序博客网 时间:2024/05/17 01:26

 pragma预处理指令略解

#pragma指令通常用于程序开发中设置编译器的状态和指示编译器完成某种特定的动作。参考了CSDN上vcforever以及另外一位网友sunhuibo的Blog,整理出一篇关于#pragma预处理指令的解释。

一. message 参数。

message 
它能够在编译信息输出窗 
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:

#pragma message(“消息文本”)

当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条
指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 
#ifdef _X86 
#pragma message(“_X86 macro activated!”) 
#endif 
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_ X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。

二. 另一个使用得比较多的#pragma参数是code_seg。格式如:

#pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] ) 
该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在.obj文件中默认的存放节
为.text节
如果code_seg没有带参数的话,则函数存放在.text节中
push (可选参数) 将一个记录放到内部编译器的堆栈中,可选参数可以为一个标识符或者节名
pop(可选参数) 将一个记录从堆栈顶端弹出,该记录可以为一个标识符或者节名
identifier (可选参数) 当使用push指令时,为压入堆栈的记录指派的一个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈 
"segment-name" (可选参数) 表示函数存放的节名
例如:
//默认情况下,函数被存放在.text节中
void func1() {                  // stored in .text
}

//将函数存放在.my_data1节中
#pragma code_seg(".my_data1")
void func2() {                  // stored in my_data1
}

//r1为标识符,将函数放入.my_data2节中
#pragma code_seg(push, r1, ".my_data2")
void func3() {                  // stored in my_data2
}

int main() {


三. #pragma once (比较常用)

这是一个比较常用的指令,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。这条指令是比较常见的。

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

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

五. #pragma warning指令
该指令允许有选择性的修改编译器的警告消息的行为
指令格式如下:
#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...]
#pragma warning( push[ ,n ] )
#pragma warning( pop )
主要用到的警告表示有如下几个:
once:只显示一次(警告/错误等)消息
default:重置编译器的警告行为到默认状态
1,2,3,4:四个警告级别
disable:禁止指定的警告信息
error:将指定的警告信息作为错误报告

如果大家对上面的解释不是很理解,可以参考一下下面的例子及说明 
#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)
在使用标准C++进行编程的时候经常会得到很多的警告信息,而这些警告信息都是不必要的提示,
所以我们可以使用#pragma warning(disable:4786)来禁止该类型的警告
在vc中使用ADO的时候也会得到不必要的警告信息,这个时候我们可以通过
#pragma warning(disable:4146)来消除该类型的警告信息

六. pragma comment(...)
该指令的格式为
#pragma comment( "comment-type" [, commentstring] ) 
该指令将一个注释记录放入一个对象文件或可执行文件中,
comment-type(注释类型):可以指定为五种预定义的标识符的其中一种
五种预定义的标识符为:
compiler:将编译器的版本号和名称放入目标文件中,本条注释记录将被编译器忽略
         如果你为该记录类型提供了commentstring参数,编译器将会产生一个警告
例如:#pragma comment( compiler )
exestr:将commentstring参数放入目标文件中,在链接的时候这个字符串将被放入到可执行文件中,
       当操作系统加载可执行文件的时候,该参数字符串不会被加载到内存中.但是,该字符串可以被
       dumpbin之类的程序查找出并打印出来,你可以用这个标识符将版本号码之类的信息嵌入到可
       执行文件中!
lib:这是一个非常常用的关键字,用来将一个库文件链接到目标文件中
常用的lib关键字,可以帮我们连入一个库文件。 
例如:
#pragma comment(lib, "user32.lib") ,类似这样的指令还是比较常见的,记得原来在VC下面写socket程序的时候,需要包含一个库文件,就是用这种方式加入的,写OpenGL的程序的时候也这样用过的。
该指令用来将user32.lib库文件加入到本工程中

linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中
       设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:
       #pragma comment(linker, "/include:__mySymbol")
你可以在程序中设置下列链接选项

/DEFAULTLIB 
/EXPORT 
/INCLUDE 
/MERGE 
/SECTION 
这些选项在这里就不一一说明了,详细信息请看msdn!

user:将一般的注释信息放入目标文件中commentstring参数包含注释的文本信息,这个注释记录将被链接器忽略
例如:
#pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ )

七. 编译优化
每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。
例如,对循环优化功能: 
#pragma loop_opt(on)     // 激活 
#pragma loop_opt(off)    // 终止 
有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,
如“Parameter xxx is never used in function xxx”,可以这样: 
#pragma warn —100         // Turn off the warning message for warning #100 
int insert_record(REC *r) 
{ /* function body */ } 
#pragma warn +100          // Turn the warning message for warning #100 back on 
函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。
每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。


八. #pragma pack 与 内存对齐问题
    许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。
    Win32平台下的微软C编译器(cl.exe for 80x86)在默认情况下采用如下的对齐规则: 
    任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),
就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。

    Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):
    任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如long,double)都以4为对齐模数。

    ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。那么结构体本身有什么对齐要求吗?有的,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。

如何使用c/c++中的对齐选项

    vc6中的编译选项有 /Zp[1|2|4|8|16] ,/Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。
n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。
也就是:
    min ( sizeof ( member ), n)
    实际上,1字节边界对齐也就表示了结构成员之间没有空洞。
    /Zpn选项是应用于整个工程的,影响所有的参与编译的结构。
    要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择Code Generation分类,在Struct member alignment可以选择。
    要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令:

(1) #pragma pack( [ n ] )
    该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由/Zp 选项设置。
紧凑对齐用pack编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。
该编译指示对定义无效。
    当你使用#pragma pack ( n ) 时, 这里n 为1、2、4、8 或16。
    第一个结构成员之后的每个结构成员都被存储在更小的成员类型或n 字节界限内。
如果你使用无参量的#pragma pack, 结构成员被紧凑为以/Zp 指定的值。该缺省/Zp 紧凑值为/Zp8 。

(2) 编译器也支持以下增强型语法:
    #pragma pack( [ [ { push | pop } , ] [ identifier, ] ] [ n] )
    若不同的组件使用pack编译指示指定不同的紧凑对齐, 这个语法允许你把程序组件组合为一个单独的转换单元。
带push参量的pack编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。
    编译指示的参量表从左到右读取。如果你使用push, 则当前紧凑值被存储起来; 
如果你给出一个n 的值, 该值将成为新的紧凑值。若你指定一个标识符, 即你选定一个名称, 
则该标识符将和这个新的的紧凑值联系起来。
    带一个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。如果你使用pop参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值, 并且将产生一个警告信息。
若你使用pop且指定一个n的值, 该值将成为新的紧凑值。若你使用p o p 且指定一个标识符, 所有存储在堆栈中的值将从栈中删除, 直到找到一个匹配的标识符, 这个与标识符相关的紧凑值也从栈中移出, 并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符, 
将使用命令行设置的紧凑值, 并且将产生一个一级警告。缺省紧凑对齐为8 。
   pack编译指示的新的增强功能让你编写头文件, 确保在遇到该头文件的前后的紧凑值是一样的。

(3) 栈内存对齐

   在vc6中栈的对齐方式不受结构成员对齐选项的影响。它总是保持对齐,而且对齐在4字节边界上

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 中型客车怎么办营业证 转租的房子押金怎么办 卖方违约中介费怎么办 房子不想租了怎么办 转租房屋室友不同意怎么办 提前不租房押金怎么办 租房提前走押金怎么办 出租车上丢东西怎么办 我有车想租出租车手续怎么办? 东西落在顺风车怎么办 滴滴投诉不处理怎么办 的士车上丢东西怎么办 手机掉在出租车怎么办 出国痰培养不合格怎么办 教师资格证体检弱视怎么办 弱视不能考驾照怎么办 驾驶证b证扣分怎么办 c1并d照怎么办 钱包落出租车上怎么办 杭州车过户车牌怎么办 哈市医保卡丢失怎么办 退休时医保怎么办手续 社保退休了医保怎么办 工作调动后医保怎么办 异地工作调动医保怎么办 工作调动社保医保怎么办 工作调动了社保怎么办 医疗卡丢了怎么办 省直医保卡丢失怎么办 医保卡挂失失败怎么办 临时社保卡过期怎么办 医保卡补办期间怎么办 医保丢了住院怎么办 临时社保卡丢失怎么办 深圳社保卡丢失怎么办 医疗保险本丢了怎么办 人不在家怎么办社保卡 人外地怎么办社保卡 卡身份证丢了怎么办 小孩医保卡丢失怎么办 医疗保险卡丢了怎么办