您的位置:首页 > 其它

#pragma用法详解

2014-11-06 21:05 183 查看
Author :Jeffrey

My Blog:http://blog.csdn.net/gueter/

目录:

(0) 前言

(1) #pragma message能够在编译信息输出窗口中输出相应的信息

(2) #pragma code_seg能够设置程序中函数代码存放的代码段,开发驱动程序的时会用到

(3) #pragma once若用在头文件的最开始处就能够保证头文件被编译一次

(4) #pragma hdrstop表示预编译头文件到此为止

(5) #pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程

(6) #pragma warning允许有选择性的修改编译器的警告消息的行为

(7) #pragma comment将一个注释记录放入一个对象文件或可执行文件中

(8) #pragma data_seg建立一个新的数据段并定义共享数据

应用1:在DLL中定义一个共享的,有名字的数据段

应用2: data_seg控制应用程序的启动次数

(9) 其他用法

(0)前言

#Pragma 指令的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma 指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。

其格式一般为: #Pragma Para

其中Para为参数,下面来看一些常用的参数。

(1) #Pragma message参数能够在编译信息输出窗口中输出相应的信息

这对于源代码信息的控制是非常重要的。其使用方法为: Pragma message(“消息文本”)

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

#ifdef _X86

#pragma message(“_X86 macro activated!”)

#endif

若定义了_X86,程序编译时就会在显示“_X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 。

(2) #pragma code_seg能够设置程序中函数代码存放的代码段,

开发驱动程序的时候就会使用到它。格式如下:

#pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ][ "segment-name" [, "segment-class" ] ])

该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序 ,如果code_seg没有带参数的话,则函数在OBJ文件中存放在默认在.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() {}

(3)#pragma once (比较常用)若用在头文件的最开始处就能够保证头文件被编译一次.

一般在整个工程中我们只要包含头文件一次就够了,若多个.c/.cpp 文件中都要包含同一个头文件,比如 Windows.h,那很多声明等等岂不是有两次了?解决这个问题的传统的方法是在头文件开始出用 #define 定义一个宏,比如 Windows.h 中:

#ifndef _WINDOWS_

#define _WINDOWS_

#endif

这样就可以避免被包含多次。但是这样的后果是代码的可读性较差 (个人观点),VC给我们提供了另外一个途径,那就是在文件的前面加上:

#pragma once”

(4)#pragma hdrstop表示预编译头文件到此为止

后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件.有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

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

(6) #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( 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将一个注释记录放入一个对象文件或可执行文件中

该指令的格式为

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

comment-type(注释类型):可以指定为五种预定义的标识符的其中一种,五种预定义的标识符为:

compiler:将编译器的版本号和名称放入目标文件中,本条注释记录将被编译器忽略,如果你为该记录类型提供了commentstring参数,编译器将会产生一个警告

例如:#pragma comment( compiler )

exestr: 链接时,将commentstring参数放入到可执行文件中,当操作系统加载可执行文件的时候,该参数字符串不会被加载到内存中.但是,该字符串可被dumpbin之类的程序查找出并打印出来,你可以用这个标识符将版本号码之类的信息嵌入到可执行文件中!

lib:用来将一个库文件链接到目标文件中

比如我们连接的时候用到了WSock32.lib,你当然可以不辞辛苦地把它加入到你的工程中。但是我觉得更方便的方法是使用#pragma指示符,指定要连接的库:

#pragma comment(lib, "WSock32.lib")

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

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

你可以在程序中设置下列链接选项

/DEFAULTLIB:library

/DEFAULTLIB 选项将一个 library 添加到 LINK 在解析引用时搜索的库列表。用 /DEFAULTLIB指定的库在命令行上指定的库之后和 .obj 文件中指定的默认库之前被搜索。忽略所有默认库 (/NODEFAULTLIB) 选项重写 /DEFAULTLIB:library。如果在两者中指定了相同的
library 名称,忽略库 (/NODEFAULTLIB:library)
选项将重写 /DEFAULTLIB:library。


/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]

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

DATA关键字指定导出项为数据项。客户程序中的数据项必须用extern __declspec(dllimport)来声明。

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

源代码中的 __declspec(dllexport).def 文件中的 EXPORTS 语句LINK 命令中的 /EXPORT 规范所有这三种方法可以用在同一个程序中。LINK 在生成包含导出的程序时还创建导入库,除非生成中使用了
.exp 文件。

LINK 使用标识符的修饰形式。编译器在创建
.obj 文件时修饰标识符。如果entryname以其未修饰的形式指定给链接器(与其在源代码中一样),则
LINK 将试图匹配该名称。如果无法找到唯一的匹配名称,则 LINK 发出错误信息。当需要将标识符指定给链接器时,请使用
Dumpbin 工具获取该标识符的修饰名形式。

/INCLUDE

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

若要指定多个符号,请在符号名称之间键入逗号 (,)、分号 (;) 或空格。在命令行上,对每个符号指定一次 /INCLUDE:symbol。

链接器通过将包含符号定义的对象添加到程序来解析 symbol。该功能对于添包含不会链接到程序的库对象非常有用。用该选项指定符号将通过 /OPT:REF 重写该符号的移除。

我们经常用到的是#pragma comment(lib,"*.lib")这类的。#pragma comment(lib,"Ws2_32.lib")表示链接Ws2_32.lib这个库。 和在工程设置里写上链入Ws2_32.lib的效果一样,不过这种方法写的 程序别人在使用你的代码的时候就不用再设置工程settings了

/MERGE

/ SECTION

这些选项在这里就不一一说明了,详细信息请看msdn!

user:将一般的注释信息放入目标文件中commentstring参数包含注释的文本信息,这个注释记录将被链接器忽略,例如:

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

(8)#pragma data_seg建立一个新的数据段并定义共享数据

格式为:

#pragma data_seg ("shareddata")

HWND sharedwnd=NULL;//共享数据

#pragma data_seg()

应用1:在DLL中定义一个共享的,有名字的数据段。

注意:a、这个数据段中的全局变量能够被多个进程共享。否则多个进程之间无法共享DLL中的全局变量。

b、共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。

假如在一个DLL中这么写:

#pragma data_seg("MyData")

int g_Value; // 全局变量未初始化

#pragma data_seg()

DLL提供两个接口函数:

int GetValue()

{

return g_Value;

}

void SetValue(int n)

{

g_Value = n;

}

然后启动两个都调用了这个DLL的进程A和B,假如A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值不一定是5,而是个未定义的值。因为DLL中的全局数据对于每一个调用他的进程而言,是私有的,不能共享的。假如您对g_Value进行了初始化,那么g_Value就一定会被放进MyData段中。

换句话说,假如A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值就一定是5!这就实现了跨进程之间的数据通信!#pragma

应用2: data_seg控制应用程序的启动次数

有的时候我们可能想让一个应用程序只启动一次,就像单件模式(singleton)一样,实现的方法可能有多种,这里说说用#pragma data_seg来实现的方法,很是简洁便利。应用程序的入口文件前面加上:

#pragma data_seg("flag_data")

int app_count = 0;

#pragma data_seg()

#pragma comment(linker,"/SECTION:flag_data,RWS")

然后程序启动的地方加上

if(app_count>0) // 如果计数大于0,则退出应用程序。

{

//MessageBox(NULL, "已经启动一个应用程序", "Warning", MB_OK);

//printf("no%d application", app_count);

return FALSE;

}

app_count++;

(9)其他用法

编译程序可以用#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的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: