#pragma 预处理指令详解

在全部的预处理指令中,#pragma 指令多是最复杂的了,它的做用是设定编译器的状态或者是指示编译器完成一些特定的动做。
#pragma指令对每一个编译器给出了一个方法,在保持与C和C++语言彻底兼容的状况下,给出主机或操做系统专有的特征。
依据定义,编译指示是机器或操做系统专有的,且对于每一个编译器都是不一样的。 
    其格式通常为: #pragma  para 
    其中para为参数,下面来看一些经常使用的参数。 
 
(1)message 参数
    message参数是我最喜欢的一个参数,它可以在编译信息输出窗口中输出相应的信息,
这对于源代码信息的控制是很是重要的。其使用方法为: 
    #pragma  message("消息文本") 
    当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 
    当咱们在程序中定义了许多宏来控制源代码版本的时候,咱们本身有可能都会忘记有没有正确的设置这些宏,
此时咱们能够用这条指令在编译的时候就进行检查。假设咱们但愿判断本身有没有在源代码的什么地方定义了_X86这个宏,
能够用下面的方法:
    #ifdef  _X86 
    #pragma  message("_X86  macro  activated!") 
    #endif 
    咱们定义了_X86这个宏之后,应用程序在编译时就会在编译输出窗口里显示"_86  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编译。
你能够用#pragma  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关键字,能够帮咱们连入一个库文件。如:
    #pragma  comment(lib, "comctl32.lib")
    #pragma  comment(lib, "vfw32.lib")
    #pragma  comment(lib, "wsock32.lib")
 
  
  
每一个编译程序能够用#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 为一、二、四、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字节边界上。
相关文章
相关标签/搜索