现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

#pragma预处理命令详解

2014-10-20 08:05 工业·编程 ⁄ 共 6231字 ⁄ 字号 暂无评论

1.#pragma once。保证所在文件只会被包含一次,它是基于磁盘文件的,而#ifndef则是基于宏的。

2.#pragma warning。允许有选择性的修改编译器的警告消息的行为。有如下用法:

#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(default:176)     // 重置编译器的176号警告行为到默认状态

同时这个pragma warning也支持如下格式,其中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)。

3.#pragma hdrstop。表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以 加快链接的速度,但如果所有头文件都进

行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文 件。

4.#pragma message。在标准输出设备中输出指定文本信息而不结束程序运行。用法如下:

#pragma message("消息文本")。当编译器遇到这条指令时就在编译输出窗口中将“消息文本”打印出来。

5.#pragma data_seg。一般用于DLL中,它能够设置程序中的初始化变量在obj文件中所在的数据段。如果未指定参数,初始化变量将放置在默认数

据段.data中,有如下用法:

1: #pragma data_seg("Shared") // 定义了数据段"Shared",其中有两个变量a和b

2: int a = 0; // 存储在数据段"Shared"中

3: int b; // 存储在数据段".bss"中,因为没有初始化

4: #pragma data_seg() // 表示数据段"Shared"结束,该行代码为可选的

对变量进行专门的初始化是很重要的,否则编译器将把它们放在普通的未初始化数据段中而不是放在shared中。如上述的变量b其实是放在了未初始化数

据段.bss中。

1: #pragma data_seg("Shared")

2: int j = 0; // 存储在数据段"Shared"中

3: #pragma data_seg(push, stack1, "Shared2") //定义数据段Shared2,并将该记录赋予别名stack1,然后放入内部编译器栈中

4: int l = 0; // 存储在数据段"Shared2"中

5: #pragma data_seg(pop, stack1) // 从内部编译器栈中弹出记录,直到弹出stack1,如果没有stack1,则不做任何操作

6: int m = 0; // 存储在数据段"Shared"中,如果没有上述pop段,则该变量将储在数据段"Shared2"中

6.#pragma code_seg。它能够设置程序中的函数在obj文件中所在的代码段。如果未指定参数,函数将放置在默认代码段.text中,有如下用法:

1: void func1() { // 默认存储在代码段.text中

2: }

3:

4: #pragma code_seg(".my_data1")

5:

6: void func2() { // 存储在代码段.my_data1中

7: }

8:

9: #pragma code_seg(push, r1, ".my_data2")

10:

11: void func3() { // 存储在代码段.my_data2中

12: }

13:

14: #pragma code_seg(pop, r1)

15:

16: void func4() { // 存储在代码段.my_data1中

17: }

7.#pragma pack。用来改变编译器的字节对齐方式。常规用法为:

#pragma pack(n)   //将编译器的字节对齐方式设为n,n的取值一般为1、2、4、8、16,一般默认为8

#pragma pack(show) //以警告信息的方式将当前的字节对齐方式输出

#pragma pack(push) //将当前的字节对齐方式放入到内部编译器栈中

#pragma pack(push,4) //将字节对齐方式4放入到内部编译器栈中,并将当前的内存对齐方式设置为4

#pragma pack(pop) //将内部编译器栈顶的记录弹出,并将其作为当前的内存对齐方式

#pragma pack(pop,4) //将内部编译器栈顶的记录弹出,并将4作为当前的内存对齐方式

#pragma pack(pop,r1) //r1为自定义的标识符,将内部编译器中的记录弹出,直到弹出r1,并将r1的值作为当前的内存对齐方式;如果r1不存在,当

不做任何操作

一个例子:

以如下结构为例: struct {

                   char a;

                   WORD b;

                   DWORD c;

                   char d;

                  }

在Windows默认结构大小: sizeof(struct) = 4+4+4+4=16;

与#pragma pack(4)一样

若设为 #pragma pack(1), 则结构大小: sizeof(struct) = 1+2+4+1=8;

若设为 #pragma pack(2), 则结构大小: sizeof(struct) = 2+2+4+2=10;

在#pragma pack(1)时:空间是节省了,但访问速度降低了;

有什么用处???

在系统通讯中,如和硬件设备通信,和其他的操作系统进行通信时等,必须保证双方的一致性。

8.#pragma comment。将一个注释记录放置到对象文件或可执行文件中。

其格式为:#pragma comment( comment-type [,"commentstring"] )。其中,comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker,user之一。

compiler:放置编译器的版本或者名字到一个对象文件,该选项是被linker忽略的。

exestr:在以后的版本将被取消。

lib:放置一个库搜索记录到对象文件中,这个类型应该与commentstring(指定Linker要搜索的lib的名称和路径)所指定的库类型一致。在对象文件中,库的名字跟在默认搜索记录后面;linker搜索这个这个库就像你在命令行输入这个命令一样。你可以在一个源文件中设置多个库搜索记录,它们在obj

文件中出现的顺序与在源文件中出现的顺序一样。

如果默认库和附加库的次序是需要区别的,使用/Zl编译开关可防止默认库放到object模块中。

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至65535;如果没有指定ordinal,则LINK将分配一个。

NONAME关键字只将函数导出为序号,没有entryname。DATA 关键字指定导出项为数据项。客户程序中的数据项必须用extern __declspec

(dllimport)来声明。

有三种导出定义的方法,按照建议的使用顺序依次为:

         

源代码中的__declspec(dllexport)

.def文件中的EXPORTS语句

LINK命令中的/EXPORT规范

所有这三种方法可以用在同一个程序中。LINK在生成包含导出的程序时还要创建导入库,除非在生成过程中使用了.exp 文件。

LINK使用标识符的修饰形式。编译器在创建obj文件时修饰标识符。如果entryname以其未修饰的形式指定给链接器(与其在源代码中一样),则LINK

将试图匹配该名称。如果无法找到唯一的匹配名称,则LINK发出错误信息。当需要将标识符指定给链接器时,请使用Dumpbin工具获取该标识符的修饰

名形式。

(3)/INCLUDE:symbol

/INCLUDE选项通知链接器将指定的符号添加到符号表。若要指定多个符号,请在符号名称之间键入逗号(,)、分号(;)或空格。在命令行上,对每个符号需指定一次/INCLUDE:symbol。

链接器通过将包含符号定义的对象添加到程序来解析symbol。该功能对于添加不会链接到程序的库对象非常有用。

用该选项所指定的符号将覆盖通过/OPT:REF对该符号进行的移除操作。

(4)/MANIFESTDEPENDENCY:manifest_dependency

/MANIFESTDEPENDENCY允许你指定位于manifest文件的<dependency>段的属性。/MANIFESTDEPENDENCY信息可以通过下面两种方式传递给LINK:

直接在命令行运行/MANIFESTDEPENDENCY

通过#pragma comment

(5)/MERGE:from=to

/MERGE选项将第一个段(from)与第二个段(to)进行联合,并将联合后的段命名为to的名称。

如果第二个段不存在,LINK将段(from)重命名为to的名称。

/MERGE选项对于创建VxDs和重写编译器生成的段名非常有用。

(6)/SECTION:name,[[!]{DEKPRSW}][,ALIGN=#]

/SECTION选项用来改变段的属性,当指定段所在的obj文件编译的时候重写段的属性集。

可移植的可执行文件(PE)中的段(section)与新可执行文件(NE)中的节区(segment)或资源大致相同。

段(section)中包含代码或数据。与节区(segment)不同的是,段(section)是没有大小限制的连续内存块。有些段中的代码或数据是你的程序直接定义和

使用的,而有些数据段是链接器和库管理器(lib.exe)创建的,并且包含了对操作系统来说很重要的信息。

/SECTION选项中的name是大小写敏感的。

不要使用以下名称,因为它们与标准名称会冲突,例如,.sdata是RISC平台使用的。

.arch

.bss

.data

.edata

.idata

.pdata

.rdata

.reloc

.rsrc

.sbss

.sdata

.srdata

.text

.xdata

为段指定一个或多个属性。属性不是大小写敏感的。对于一个段,你必须将希望它具有的属性都进行指定;如果某个属性未指定,则认为是不具备这个属

性。如果你未指定R,W或E,则已存在的读,写或可执行状态将不发生改变。

要对某个属性取否定意义,只需要在属性前加感叹号(!)。

E:可执行的

R:可读取的

W:可写的

S:对于载入该段的镜像的所有进程是共享的

D:可废弃的

K:不可缓存的

P:不可分页的

注意K和P是表示否定含义的。

PE文件中的段如果没有E,R或W属性集,则该段是无效的。

ALIGN=#选项让你为一个具体的段指定对齐值。

user:放置一个常规注释到一个对象文件中,该选项是被linker忽略的。

9.#pragma section。创建一个段。

其格式为:#pragma section( "section-name" [, attributes] )

section-name是必选项,用于指定段的名字。该名字不能与标准段的名字想冲突。可用/SECTION查看标准段的名称列表。

attributes是可选项,用于指定段的属性。可用属性如下,多个属性间用逗号(,)隔开:

read:可读取的

write:可写的

execute:可执行的

shared:对于载入该段的镜像的所有进程是共享的

nopage:不可分页的,主要用于Win32的设备驱动程序中

nocache:不可缓存的,主要用于Win32的设备驱动程序中

discard:可废弃的,主要用于Win32的设备驱动程序中

remove:非内存常驻的,仅用于虚拟设备驱动(VxD)中

如果未指定属性,默认属性为read和write。

在创建了段之后,还要使用__declspec(allocate)将代码或数据放入段中。

例如:

//pragma_section.cpp

#pragma section("mysec",read,write)

int j = 0;

__declspec(allocate("mysec"))

int i = 0;

int main(){}

该例中, 创建了段"mysec",设置了read,write属性。但是j没有放入到该段中,而是放入了默认的数据段中,因为它没有使用__declspec(allocate)进

行声明;而i放入了该段中,因为使用__declspec(allocate)进行了声明。

10.#pragma push_macro与#pragma pop_macro

前者将指定的宏压入栈中,相当于暂时存储,以备以后使用;后者将栈顶的宏出栈,弹出的宏将覆盖当前名称相同的宏。例如:

#include <stdio.h>

#define X 1

#define Y 2

int main() {

printf("%d",X);

printf("\n%d",Y);

  #define Y 3   // C4005

#pragma push_macro("Y")

#pragma push_macro("X")

printf("\n%d",X);

#define X 2   // C4005

printf("\n%d",X);

#pragma pop_macro("X")

printf("\n%d",X);

#pragma pop_macro("Y")

  printf("\n%d",Y);

}

输出结果:

1

2

1

2

1

3

给我留言

留言无头像?