前言
最近在學習#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編譯指令大全(下)