前言
最近在学习#pragma编译指令相关的知识,网上也有很多文章介绍各个指令的用法,但是在网上搜到的对#pragma总结的文章都不够全面,看到最多的一个版本也就包含了20多条指令。所以我就通过查阅MSDN官方的说明文档,然后根据自己的理解对现有的#pragma编译指令进行了梳理和总结。希望能够帮助大家对#pragma系列指令有更好的理解。
由于全文太长,无法放在一片文章发表,所以就拆分成上下两部分
综述
#pragma指令是每个编译器在保留C和C++语言的整体兼容性时提供不同机器和操作系统特定的功能。编译指令是机器或操作系统特有的,并且不同的编译器通常存在差异。
语法如下:
#pragma token_string // token_string为参数
“#”必须是编译指令的第一个非空白字符;而“#”和“pragma”之间可以存在任意个数的空白符。在#pragma后面,写任何编译器能够作为预处理符号分析的文本。#pragma的参数类似于宏扩展。如果参数无法识别,编译器会抛出一个警告后继续编译。示例代码如下:
#pragma once // 正确
#pragma once // 正确
# pragma once // 正确
;#pragma once // 错误
ps: error C2014: preprocessor command must start as first nonwhite space
为了提供新的预处理功能,或者为编译程序提供由实现定义的信息,编译指示可以用在一个条件语句内。
C和C++编译器可以识别下列编译指令。
row1 | row2 | row3 | row4 |
---|---|---|---|
alloc_text | auto_inline | bss_seg | check_stack |
code_seg | comment | component | conform |
const_seg | data_seg | deprecated | detect_mismatch |
fenv_access | float_control | fp_contract | function |
hdrstop | include_alias | init_seg | inline_depth |
inline_recursion | intrinsic | loop | make_public |
managed message | omp | once | optimize |
pack | pointers_to_members | pop_macro | push_macro |
region, endregion | runtime_checks | section | setlocale |
strict_gs_check | unmanaged | vtordisp | warning |
alloc_text
语法
#pragma alloc_text( "textsection", function1, ... )
作用
命名特定函数驻留的代码段。
备注
- 必须出现在函数声明和函数定义之间。
- 不处理C++成员函数或重载函数。它仅能应用在以C连接方式声明的函数(用extern "C"连接)。如果将该指令运用在具有C++连接方式的函数时,将出现编译错误。
- 由于函数寻址不支持__based形式,所以需要通过该编译指令来指定代码段。textsection指定的名字应该由双引号括起来。
- 引用的函数必须与该指令处于同一模块中,否则可能无法捕获编译器将未定义的函数编译到不同的代码段中的错误,即使程序通常还能正常运行,但是函数并没有分配到指定的代码段中。
- 该编译指令不能用在一个函数体内部。
auto_inline
语法
#pragma auto_inline( [{on | off}] )
作用
打开(on)或关闭(off)编译器将普通函数自动转换为inline函数的功能。
备注
- 不能出现在函数定义内部,需要写在函数定义之前或之后。
- 将在其出现以后的第一个函数定义开始起作用。
- 对显式的inline函数不起作用。
bss_seg
语法
#pragma bss_seg( [ [ { push | pop }, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
作用
指定obj文件中存放未初始化变量的段。
备注
- obj文件可以通过dumpbin来查看。
- obj中存放未初始化变量的默认段为
.bss
。 - 在一些情况下将未初始化变量存储在一个段中可以提高加载速度。
- 不带参数的bss_egg将段重置为
.bss
。 - push: 将一条段记录压入编译堆栈,可以带identifier和segment-name参数。
- pop: 将编译堆栈顶的记录弹出。
- identifer: 可选参数,当使用push时指定压入编译堆栈的记录标示符,当使用pop时,弹出从栈顶到该标示符记录之间的所有元素。它可以使一条pop指令弹出多条push记录。
- segment-name: 段名,当使用pop指令之后,弹出的段名将作为新的激活段。
- segment-class: 段类,用于兼容C++2.0之前的版本,已废弃。
代码示例
// pragma_directive_bss_seg.cpp
int i; // stored in .bss
#pragma bss_seg(".my_data1")
int j; // stored in "my_data1"
#pragma bss_seg(push, stack1, ".my_data2")
int l; // stored in "my_data2"
// pop stack1 from stack
#pragma bss_seg(pop, stack1)
int m; // stored in "stack_data1"
int main() {}
check_stack
语法
#pragma check_stack([ {on | off}] )
#pragma check_stack{+ | –}
作用
打开(on/+)或关闭(off/-)栈检查。
备注
- check_stack在无参情况下栈检查恢复到默认行为,详细情况见编译器参考。
- 该编译指令将在其出现之后的第一个函数开始生效。
- 栈检查不是宏或者inline函数的一部分。
- #pragma check_stack和/Gs选项的互相作用情况如下所示。
Syntax | Compiled with/Gs option? | Action |
---|---|---|
#pragma check_stack( ) or #pragma check_stack | Yes | Turns off stack checking for functions that follow |
#pragma check_stack( ) or #pragma check_stack | No | Turns on stack checking for functions that follow |
#pragma check_stack(on)or #pragma check_stack + | Yes or No | Turns on stack checking for functions that follow |
#pragma check_stack(off)or #pragma check_stack – | Yes or No | Turns off stack checking for functions that follow |
表格中前面两条的Action总感觉弄反了,待确认
code_seg
语法
#pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
作用
指定obj文件中存放函数的代码段。
备注
- obj文件中存放代码的默认段是
.text
。 - 其它用法与bss_seg类似。
代码示例
// pragma_directive_code_seg.cpp
void func1() {} // stored in .text
#pragma code_seg(".my_data1")
void func2() {} // stored in my_data1
#pragma code_seg(push, r1, ".my_data2")
void func3() {} // stored in my_data2
}
#pragma code_seg(pop, r1)
void func4() {} // stored in my_data1
}
int main() {}
comment
语法
#pragma comment( comment-type [, commentstring] )
作用
将描述记录嵌入到目标文件或可执行文件中。
备注
- comment-type是一个预定义标识符,用于指定注释的类型,是compiler,exestr,lib,linker和user五个标示符之一。
- commentstring是可选字段,用于为一些comment-type提供附加的信息。因为commentstring是一个字符串,所以它适用字符串的所有规则,例如换码字符、嵌入的引号(")和联接。
compiler
- 将编译器名称和版本号信息存放到目标文件中,连接器(linker)会忽略该描述记录。
- 如果compiler提供一个commentstring参数,编译程序将生成一个警告。
#pragma comment( compiler )
exestr
- 将commentstring存放到目标文件中去,并且在连接时存放到可执行文件去中。
- 可执行文件运行后不会加载该字符串到内存,但是该字符串可以被能够在文件中搜索可打印字符串的程序检索到。
- 该描述记录的一个用处是在可执行文件中嵌入版本号或者类似的信息。
#pragma comment( exestr, "test" )
lib
- 将静态链接库信息放置到目标文件中去。
- 该描述类型必须有commentstring参数,其中包含静态链接库的名称或路径。
- 该库名放在目标文件中默认库搜索信息之后,linker会像在命令行中输入一样搜索该库名。
- 可以在一个源文件中放置多个库搜索信息,并且按照它们出现在源文件中的顺序存放在目标文件中。
#pragma comment( lib, "emapi" )
linker
- 在目标文件中放置连接程序选项。
- 可以用它指定连接程序选项来代替在Project Setting对话框中Link页内的选项。例如,你可以指定/include选项以强制包含一个符号:
#pragma comment(linker, "/include:__mySymbol")
- 只有下列选项可用于linker标识符
/DEFAULTLIB
/EXPORT
/INCLUDE
/MANIFESTDEPENDENCY
/MERGE
/SECTION
user
- 将普通描述信息存放在目标文件中。commentstring参数包含描述的文本。linker将忽略该描述信息。
- commentstring可以嵌套连接字符串宏。
#pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ )
component
语法
#pragma component( browser, { on | off }[, references [, name ]] )
#pragma component( minrebuild, on | off )
#pragma component( mintypeinfo, on | off )
作用
控制对源文件中浏览信息或依赖信息的收集。
备注
浏览器(Browser)
- 可以打开或关闭收集,也可以指定在收集信息时忽略特定名称或类型。
- 若要打开浏览信息的收集,必须先启用浏览信息功能。
- references选项可以有也可以没有name参数。使用没有name参数的references选项将打开或者关闭引用信息的收集(然而继续收集其它浏览信息)。
- 使用有name和off参数的references选项将阻止从浏览信息窗口中出现引用到的名字。用这个语法将忽略你不感兴趣的名字和类型从而减少浏览信息文件的大小。
- 若要防止预处理器扩展name(如将NULL扩展到0),请使用引号将其包含。
// 关闭收集浏览信息
#pragma component(browser, off)
// 关闭收集引用信息
#pragma component(browser, off, references)
// 关闭收集DWORD类型的引用信息
#pragma component(browser, off, references, DWORD)
// 打开收集DWORD类型的引用信息
#pragma component(browser, on, references, DWORD)
// 防止预处理器扩展name(如:NULL扩展为0)
#pragma component(browser, off, references, "NULL")
最小化重建(Minimal Rebuild)
- Visual C++的最小化重建功能要求编译器创建并保存需要大量磁盘空间的C++类依赖信息。
- 使用
#pragma component(minrebuild,off)
关闭信息收集。 - 使用
#pragma component(minrebuild,on)
重新打开依赖信息。
减少类型信息(Reduce Type Information)
- mintypeinfo选项将减少指定区域的调试信息。此信息的量相当大,会影响.pdb和.obj文件。
- 不能在mintypeinfo区域中调试类和结构。
- 使用mintypeinfo选项可帮助避免以下警告:
LINK : warning LNK4018: too many type indexes in PDB "filename", discarding subsequent type information
更多详细内容,请参阅Enable Minimal Rebuild(/Gm)编译程序选项。
conform
语法
#pragma conform(name [, show ] [, on | off ] [ [, push | pop ] [, identifier ] ] )
作用
指定/Zc:forScope编译器选项的运行时行为。
备注
- name:指定要修改的编译器选项的名称。 唯一有效的name为forScope。
- show(可选): 将name的当前设置(true或false)在编译期间以警告消息的方式显示。
- on、off(可选): 将name设置为on将启用/Zc:forScope编译器选项。默认值为off。
- push(可选): 将name的当前值推送到内部编译器堆栈。如果指定identifier,则可为要推送到堆栈的name指定on或off值。
- pop(可选):将name的值设置为位于内部编译器堆栈顶部的值,然后弹出堆栈。 如果使用pop指定 identifier,则堆栈将弹回,直到它找到具有identifier的记录(也会弹出);堆栈上的下一记录中的 name的当前值将变为name的新值。如果使用不在堆栈上的记录中的identifier指定pop,则将忽略 pop。
- identifier(可选):可以与push或pop命令包含在一起。如果使用了identifier,则还可以使用 on或off说明符。
示例代码
// pragma_directive_conform.cpp
// compile with: /W1
// C4811 expected
#pragma conform(forScope, show)
#pragma conform(forScope, push, x, on)
#pragma conform(forScope, push, x1, off)
#pragma conform(forScope, push, x2, off)
#pragma conform(forScope, push, x3, off)
#pragma conform(forScope, show)
#pragma conform(forScope, pop, x1)
#pragma conform(forScope, show)
int main() {}
const_seg
语法
#pragma const_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
作用
指定obj文件中存放const变量的段。
备注
- obj文件中存放const变量的默认段为
.rdata
。 - 某些const变量(如标量)会自动内敛到代码流中。
- 内敛代码将不会存放在
.rdata
中。 - 在const_seg中定义需要动态初始化的对象会导致未定义的行为。
- 不带参数的#pragma const_seg会将段重置为
.rdata
。
示例代码
// pragma_directive_const_seg.cpp
// compile with: /EHsc
#include <iostream>
const int i = 7; // inlined, not stored in .rdata
const char sz1[]= "test1"; // stored in .rdata
#pragma const_seg(".my_data1")
const char sz2[]= "test2"; // stored in .my_data1
#pragma const_seg(push, stack1, ".my_data2")
const char sz3[]= "test3"; // stored in .my_data2
#pragma const_seg(pop, stack1) // pop stack1 from stack
const char sz4[]= "test4"; // stored in .my_data1
int main() {
using namespace std;
// const data must be referenced to be put in .obj
cout << sz1 << endl;
cout << sz2 << endl;
cout << sz3 << endl;
cout << sz4 << endl;
}
data_seg
语法
#pragma data_seg( [ [ { push | pop }, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
作用
指定obj文件中存放初始化变量的数据段。
备注
- obj文件中存放初始化变量的默认段为
.data
。 - 未初始化的变量被视为初始化为零并且存储在
.bss
中。 - 不带参数的data_seg将段重置为
.data
。
示例代码
// pragma_directive_data_seg.cpp
int h = 1; // stored in .data
int i = 0; // stored in .bss
#pragma data_seg(".my_data1")
int j = 1; // stored in "my_data1"
#pragma data_seg(push, stack1, ".my_data2")
int l = 2; // stored in "my_data2"
#pragma data_seg(pop, stack1) // pop stack1 off the stack
int m = 3; // stored in "stack_data1"
int main() {}
deprecated
语法
#pragma deprecated( identifier1 [,identifier2, ...] )
作用
指示函数、类型或任何其他标识符已废弃。
备注
- 当编译器遇到废弃的符号时,会抛出警告C4995。
- 可以废弃宏名称。但是需要将宏名称包含在引号内,否则宏将展开。
- 可以使用deprecated __declspec废弃重载函数。
示例代码
// pragma_directive_deprecated.cpp
// compile with: /W3
#include <stdio.h>
void func1(void) {}
void func2(void) {}
int main() {
func1();
func2();
#pragma deprecated(func1, func2)
func1(); // C4995
func2(); // C4995
}
// pragma_directive_deprecated2.cpp
// compile with: /W3
#pragma deprecated(X)
class X { // C4995
public:
void f(){}
};
int main() {
X x; // C4995
}
detect_mismatch
语法
#pragma detect_mismatch( "name", "value"))
作用
将记录放在一个对象中。 链接器将检查这些记录中的潜在不匹配项。
备注
- link项目时,如果项目包含name相同但value不同的两个对象,则linker将抛出错误LNK2038。
- 使用detect_mismatch可防止连接中存在不一致的对象文件。
- 名称和值都是字符串,遵循转义字符和连接的字符串规则。 同时区分大小写,不能包含逗号、等号、引号或null字符。
示例代码
// pragma_directive_detect_mismatch_a.cpp
#pragma detect_mismatch("myLib_version", "9")
int main ()
{
return 0;
}
// pragma_directive_detect_mismatch_b.cpp
#pragma detect_mismatch("myLib_version", "1")
如果使用命令行 cl pragma_directive_detect_mismatch_a.cpp pragma_directive_detect_mismatch_b.cpp 编译这两个文件,则会收到错误 LNK2038。
execution_character_set
语法
#pragma execution_character_set("target")
作用
指定运行时字符集。
备注
- target指定字符集名称,目前只支持“utf-8”;
- 该指令在Visual Studio 2015 Updatae 2中废弃了。建议使用编译选项/execution-charset:utf-8或/utf-8配合u8前缀来指定字符集。
- 默认情况下编译器采用系统当前字符集对源文件进行编码。
- 在未指定编码集的情况下在不同的电脑上进行编译可能会引起编译警告和错误。
示例代码
#pragma execution_character_set("utf-8")
fenv_access
语法
#pragma fenv_access [ON | OFF]
作用
禁用(ON)或启用(OFF)可能更改标记测试和模式更改的优化。
备注
- 默认情况下,fenv_access为OFF。
- 有关浮点行为的详细信息,请参阅/fp(指定浮点行为)
- fenv_access的优化类型有如下几种。
a. 全局公共子表达式消除
b. 代码移动
c. 常量折叠
示例代码
// pragma_directive_fenv_access_x86.cpp
// compile with: /O2
// processor: x86
#include <stdio.h>
#include <float.h>
#include <errno.h>
#pragma fenv_access (on)
int main() {
double z, b = 0.1, t = 0.1;
unsigned int currentControl;
errno_t err;
err = _controlfp_s(¤tControl, _PC_24, _MCW_PC);
if (err != 0) {
printf_s("The function _controlfp_s failed!\n");
return -1;
}
z = b * t;
printf_s ("out=%.15e\n",z);
}
out=9.999999776482582e-003
注释掉#pragma fenv_access (on)
之后,由于编译器执行编译时计算,这个过程未使用控制模式,因此输出不同。
out=1.000000000000000e-002
float_control
语法
float_control( value,setting [push] | push | pop )
作用
指定函数的浮点行为。
备注
- value: 可以是precise或except。
- setting: 可以是on或off。
- 如果vaule是precise,则设置precise和except为setting的值。
- except只有在precise为on的情况下设置为on。
- push: 将当前的float_control设置压入编译器内部堆栈。
- pop: 从内部编译器堆栈顶部移除float_control设置,使其成为新的float_control设置。
- 当except打开时,无法关闭float_control precise。同样,当fevn_access打开时,无法关闭precise。
示例代码
// 从严格模式转换到快速模式
#pragma float_control(except, off)
#pragma fenv_access(off)
#pragma float_control(precise, off)
// The following line is needed on Itanium processors
#pragma fp_contract(on)
// 从快速模式转换到严格模式
#pragma float_control(precise, on)
#pragma fenv_access(on)
#pragma float_control(except, on)
// The following line is needed on Itanium processors.
#pragma fp_contract(off)
// 捕获浮点数溢出异常
// pragma_directive_float_control.cpp
// compile with: /EHa
#include <stdio.h>
#include <float.h>
double func( ) {
return 1.1e75;
}
#pragma float_control (except,on)
int main( ) {
float u[1];
unsigned int currentControl;
errno_t err;
err = _controlfp_s(¤tControl, ~_EM_OVERFLOW, _MCW_EM);
if (err != 0)
printf_s("_controlfp_s failed!\n");
try {
u[0] = func();
printf_s ("Fail");
return(1);
}
catch (...) {
printf_s ("Pass");
return(0);
}
}
fp_contract
语法
#pragma fp_contract [ON | OFF]
作用
决定是否使用浮点数缩写形式。
备注
- 默认情况下,fp_contract处于打开状态。
示例代码
// pragma_directive_fp_contract.cpp
// compile with: /O2
#include <stdio.h>
#include <float.h>
#pragma fp_contract (off)
int main() {
double z, b, t;
for (int i = 0; i < 10; i++) {
b = i * 5.5;
t = i * 56.025;
_set_controlfp(_PC_24, _MCW_PC);
z = t * i + b;
printf_s ("out=%.15e\n", z);
}
}
out=0.000000000000000e+000
out=6.152500152587891e+001
out=2.351000061035156e+002
out=5.207249755859375e+002
out=9.184000244140625e+002
out=1.428125000000000e+003
out=2.049899902343750e+003
out=2.783724853515625e+003
out=3.629600097656250e+003
out=4.587524902343750e+003
function
语法
#pragma function( function1 [, function2, ...] )
作用
指定对function参数列表中的函数进行显示函数调用。
备注
- 如果使用intrinsic编译指令(或/Oi编译选项)指示编译生成内敛函数(内敛函数如同嵌入代码一样生成,不作为一个函数调用),则可以使用function编译指令来实现显示函数调用。
- 一旦设定了function编译指令,它将在包含指定函数的第一个函数定义中生效。该效果持续到源文件结尾或遇到instrsic编译指令指定相同的函数。
- function仅能在函数的外部使用- 全局级别。
示例代码
// pragma_directive_function.cpp
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// use intrinsic forms of memset and strlen
#pragma intrinsic(memset, strlen)
// Find first word break in string, and set remaining
// chars in string to specified char value.
char *set_str_after_word(char *string, char ch) {
int i;
int len = strlen(string); /* NOTE: uses intrinsic for strlen */
for(i = 0; i < len; i++) {
if (isspace(*(string + i)))
break;
}
for(; i < len; i++)
*(string + i) = ch;
return string;
}
// do not use strlen intrinsic
#pragma function(strlen)
// Set all chars in string to specified char value.
char *set_str(char *string, char ch) {
// Uses intrinsic for memset, but calls strlen library function
return (char *) memset(string, ch, strlen(string));
}
int main() {
char *str = (char *) malloc(20 * sizeof(char));
strcpy_s(str, sizeof("Now is the time"), "Now is the time");
printf("str is '%s'\n", set_str_after_word(str, '*'));
printf("str is '%s'\n", set_str(str, '!'));
}
str is 'Now************'
str is '!!!!!!!!!!!!!!!'
hdrstop
语法
#pragma hdrstop [( "filename" )]
作用
提供对预编译文件名和编译状态的保存位置的额外控制。
备注
- filename是要使用或创建的预编译头文件的名称(取决于是否指定/Yu或/Yc)。
- 如果filename不包含路径说明,则假定预编译头文件与源文件处于同一目录中。
- 当用/Yc编译时,如果C或C++文件中包含了一个hdrstop编译指令,则编译器保存编译指令之前的编译状态。编译指令之后的编译状态不被保存。
- filename指定了预编译状态保存的文件名。文件名是字符串,因此受C/C++的字符串约束。必须通过引号同时使用转义符(反斜杠)来指定目录名称。例如:
#pragma hdrstop( "c:\\projects\\include\\myinc.pch" )
- 预编译头文件的名称根据以下规则按优先顺序决定:
a. /Fp编译选项的参数
b. #pragma hdrstop 的 filename 参数
c. 扩展名为.PCH的源文件基名称 - 如果/Yc和/Yu两个编译选项以及hdrstop编译指令都未指定文件名,则将源文件的基名称用作预编译头文件的名称。
- 可以使用预处理命令来执行宏替换,如下所示:
#define INCLUDE_PATH "c:\\progra~`1\\devstsu~1\\vc\\include\\"
#define PCH_FNAME "PROG.PCH"
.
.
.
#pragma hdrstop( INCLUDE_PATH PCH_FNAME )
- hdrstop必须出现在任何数据或函数声明/定义的外部。
- hdrstop必须在源文件而不是头文件中指定。
示例代码
#include <windows.h> // Include several files
#include "myhdr.h"
__inline Disp( char *szToDisplay ) // Define an inline function
{
... // Some code to display string
}
#pragma hdrstop
include_alias
语法
#pragma include_alias( "long_filename", "short_filename" )
#pragma include_alias( <long_filename>, <short_filename> )
作用
指定shot_filename用于long_filename的别名。
备注
- 有些文件系统允许长度超过8.3 FAT文件系统限制的文件名。因为较长的头文件名的前8个字符可能不是唯一的,因此编译器不能简单的将较长的名称截断为8.3。
- 当编译器遇到long_filename字符串是,都将用short_filename进行替换,并改为查找shot_filename头文件。
- 该指令必须在#include指令之前出现。
// First eight characters of these two files not unique.
#pragma include_alias( "AppleSystemHeaderQuickdraw.h", "quickdra.h" )
#pragma include_alias( "AppleSystemHeaderFruit.h", "fruit.h" )
#pragma include_alias( "GraphicsMenu.h", "gramenu.h" )
#include "AppleSystemHeaderQuickdraw.h"
#include "AppleSystemHeaderFruit.h"
#include "GraphicsMenu.h"
- 别名必须要完全符合规范,包括大小写,拼写,双引号和尖括号的使用。
- include_alias对文件名进行简单的字符串匹配,不会对文件名进行其它校验。下面代码将不执行别名替换操作。
#pragma include_alias("mymath.h", "math.h")
#include "./mymath.h"
#include "sys/mymath.h"
- include_alias不会替换/Yu和/Yc以及hdrstop指令参数的头文件名。
// 编译选项
/YcAppleSystemHeaderStop.h
// 头文件包含
#include <AppleSystemHeaderStop.h>
- 可以使用include_alias将任何头文件名映射到另一个头文件名。
#pragma include_alias( "api.h", "c:\version1.0\api.h" )
#pragma include_alias( <stdio.h>, <newstdio.h> )
#include "api.h"
#include <stdio.h>
- 不要将用双引号括起来的文件名与用尖括号括起的文件名混淆使用。例如,给定上述两个#pragma include_alias指令,编译器将不对下列 #include 指令执行任何替换:
#include <api.h>
#include "stdio.h"
- 以下指令将报错。
#pragma include_alias(<header.h>, "header.h") // Error
- 错误消息中提示的文件名(或作为预定义的 FILE 宏的值的文件名)是文件在执行替换后的名称。
#pragma include_alias( "VeryLongFileName.H", "myfile.h" )
#include "VeryLongFileName.H"
myfile.h(15) : error C2059 : syntax error
- include_alias不支持传递性。
#pragma include_alias( "one.h", "two.h" )
#pragma include_alias( "two.h", "three.h" )
#include "one.h"
编译器将搜索文件two.h而不是three.h。
init_seg
语法
#pragma init_seg({ compiler | lib | user | "section-name" [, func-name]} )
作用
指定影响启动代码的执行顺序的关键字或代码段。
备注
- 本节中segment和section的含义是可互换的。
- 由于全局静态对象的初始化可能涉及代码执行,因此,必须指定一个关键字用于确定对象的构造时间。
- init_seg在动态链接库(DLL)或需要初始化的库中使用特别重要。
- init_seg的选项如下:
a. compiler: 保留给Microsoft C运行库初始化使用,该组中的对象会优先构造。
b. lib: 用于第三方类库的初始化,该组中的对象在compiler之后构造。
c. user: 可供任何用户使用,该组中的对象最后构造。
d. section-name: 显式指定初始化的段名。该段中的对象通过显式构造函数构造,并且对象地址存放在该段中。该段中存放着用于初始化该段之后的全局变量的辅助函数指针。
e. func-name: 指定程序退出时替换atexit的函数。在自己的退出函数中可以控制不同模块的析构顺序。这个函数必须具有和atexit函数相同的形式:
int funcname(void (__cdecl *)(void));
- 可以通过显式指定段名称来延迟对象的初始化,但是必须为每个静态对象显式调用构造函数进行初始化。
- func-name不需要用引号包含。
- 各个对象存放在由xxx_seg编译指令指定的段中。
- 默认情况下,init_seg部分是只读的,如果段名称是
.CRT
,则编译器会自动将段属性修改为只读,即使段标志为读写。 - 每个源文件中只能出现一次init_seg。
- 模块中声明的对象不会由C运行时自动初始化,需要我们主动调用。如果对象没有用户定义的构造函数,系统会生成默认构造函数,但是还是需要我们主动调用。
示例代码
// pragma_directive_init_seg.cpp
#include <stdio.h>
#pragma warning(disable : 4075)
typedef void (__cdecl *PF)(void);
int cxpf = 0; // number of destructors we need to call
PF pfx[200]; // pointers to destructors.
int myexit (PF pf) {
pfx[cxpf++] = pf;
return 0;
}
struct A {
A() { puts("A()"); }
~A() { puts("~A()"); }
};
struct B {
B() { puts("B()"); }
~B() { puts("~B()"); }
};
struct C {
C() { puts("C()"); }
~C() { puts("~C()"); }
};
// ctor & dtor called by CRT startup code
// because this is before the pragma init_seg
A aaaa;
// The order here is important.
// Section names must be 8 characters or less.
// The sections with the same name before the $
// are merged into one section. The order that
// they are merged is determined by sorting
// the characters after the $.
// InitSegStart and InitSegEnd are used to set
// boundaries so we can find the real functions
// that we need to call for initialization.
#pragma section(".mine$a", read)
__declspec(allocate(".mine$a")) const PF InitSegStart = (PF)1;
#pragma section(".mine$z",read)
__declspec(allocate(".mine$z")) const PF InitSegEnd = (PF)1;
// The comparison for 0 is important.
// For now, each section is 256 bytes. When they
// are merged, they are padded with zeros. You
// can't depend on the section being 256 bytes, but
// you can depend on it being padded with zeros.
void InitializeObjects () {
const PF *x = &InitSegStart;
for (++x ; x < &InitSegEnd ; ++x)
if (*x) (*x)();
}
void DestroyObjects () {
while (cxpf>0) {
--cxpf;
(pfx[cxpf])();
}
}
// by default, goes into a read only section
#pragma init_seg(".mine$m", myexit)
B bbbb;
C cccc;
int main () {
InitializeObjects();
DestroyObjects();
}
A()
B()
C()
~C()
~B()
A()
剩余编译指令内容请阅读#pragma编译指令大全(下)