前言
最近在學(xué)習(xí)#pragma編譯指令相關(guān)的知識(shí),網(wǎng)上也有很多文章介紹各個(gè)指令的用法,但是在網(wǎng)上搜到的對(duì)#pragma總結(jié)的文章都不夠全面,看到最多的一個(gè)版本也就包含了20多條指令。所以我就通過查閱MSDN官方的說明文檔,然后根據(jù)自己的理解對(duì)現(xiàn)有的#pragma編譯指令進(jìn)行了梳理和總結(jié)。希望能夠幫助大家對(duì)#pragma系列指令有更好的理解。
由于全文太長,無法放在一片文章發(fā)表,所以就拆分成上下兩部分
綜述
#pragma指令是每個(gè)編譯器在保留C和C++語言的整體兼容性時(shí)提供不同機(jī)器和操作系統(tǒng)特定的功能。編譯指令是機(jī)器或操作系統(tǒng)特有的,并且不同的編譯器通常存在差異。
語法如下:
#pragma token_string // token_string為參數(shù)
“#”必須是編譯指令的第一個(gè)非空白字符;而“#”和“pragma”之間可以存在任意個(gè)數(shù)的空白符。在#pragma后面,寫任何編譯器能夠作為預(yù)處理符號(hào)分析的文本。#pragma的參數(shù)類似于宏擴(kuò)展。如果參數(shù)無法識(shí)別,編譯器會(huì)拋出一個(gè)警告后繼續(xù)編譯。示例代碼如下:
#pragma once // 正確
#pragma once // 正確
# pragma once // 正確
;#pragma once // 錯(cuò)誤
ps: error C2014: preprocessor command must start as first nonwhite space
為了提供新的預(yù)處理功能,或者為編譯程序提供由實(shí)現(xiàn)定義的信息,編譯指示可以用在一個(gè)條件語句內(nèi)。
請(qǐng)參閱Pragma 指令和 __Pragma 關(guān)鍵字
C和C++編譯器可以識(shí)別下列編譯指令。
| 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, ... )
作用
命名特定函數(shù)駐留的代碼段。
備注
- 必須出現(xiàn)在函數(shù)聲明和函數(shù)定義之間。
- 不處理C++成員函數(shù)或重載函數(shù)。它僅能應(yīng)用在以C連接方式聲明的函數(shù)(用extern "C"連接)。如果將該指令運(yùn)用在具有C++連接方式的函數(shù)時(shí),將出現(xiàn)編譯錯(cuò)誤。
- 由于函數(shù)尋址不支持__based形式,所以需要通過該編譯指令來指定代碼段。textsection指定的名字應(yīng)該由雙引號(hào)括起來。
- 引用的函數(shù)必須與該指令處于同一模塊中,否則可能無法捕獲編譯器將未定義的函數(shù)編譯到不同的代碼段中的錯(cuò)誤,即使程序通常還能正常運(yùn)行,但是函數(shù)并沒有分配到指定的代碼段中。
- 該編譯指令不能用在一個(gè)函數(shù)體內(nèi)部。
auto_inline
語法
#pragma auto_inline( [{on | off}] )
作用
打開(on)或關(guān)閉(off)編譯器將普通函數(shù)自動(dòng)轉(zhuǎn)換為inline函數(shù)的功能。
備注
- 不能出現(xiàn)在函數(shù)定義內(nèi)部,需要寫在函數(shù)定義之前或之后。
- 將在其出現(xiàn)以后的第一個(gè)函數(shù)定義開始起作用。
- 對(duì)顯式的inline函數(shù)不起作用。
bss_seg
語法
#pragma bss_seg( [ [ { push | pop }, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
作用
指定obj文件中存放未初始化變量的段。
備注
- obj文件可以通過dumpbin來查看。
- obj中存放未初始化變量的默認(rèn)段為
.bss。 - 在一些情況下將未初始化變量存儲(chǔ)在一個(gè)段中可以提高加載速度。
- 不帶參數(shù)的bss_egg將段重置為
.bss。 - push: 將一條段記錄壓入編譯堆棧,可以帶identifier和segment-name參數(shù)。
- pop: 將編譯堆棧頂?shù)挠涗洀棾觥?/li>
- identifer: 可選參數(shù),當(dāng)使用push時(shí)指定壓入編譯堆棧的記錄標(biāo)示符,當(dāng)使用pop時(shí),彈出從棧頂?shù)皆摌?biāo)示符記錄之間的所有元素。它可以使一條pop指令彈出多條push記錄。
- segment-name: 段名,當(dāng)使用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/+)或關(guān)閉(off/-)棧檢查。
備注
- check_stack在無參情況下棧檢查恢復(fù)到默認(rèn)行為,詳細(xì)情況見編譯器參考。
- 該編譯指令將在其出現(xiàn)之后的第一個(gè)函數(shù)開始生效。
- 棧檢查不是宏或者inline函數(shù)的一部分。
- #pragma check_stack和/Gs選項(xiàng)的互相作用情況如下所示。
| 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總感覺弄反了,待確認(rèn)
code_seg
語法
#pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
作用
指定obj文件中存放函數(shù)的代碼段。
備注
- obj文件中存放代碼的默認(rèn)段是
.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] )
作用
將描述記錄嵌入到目標(biāo)文件或可執(zhí)行文件中。
備注
- comment-type是一個(gè)預(yù)定義標(biāo)識(shí)符,用于指定注釋的類型,是compiler,exestr,lib,linker和user五個(gè)標(biāo)示符之一。
- commentstring是可選字段,用于為一些comment-type提供附加的信息。因?yàn)閏ommentstring是一個(gè)字符串,所以它適用字符串的所有規(guī)則,例如換碼字符、嵌入的引號(hào)(")和聯(lián)接。
compiler
- 將編譯器名稱和版本號(hào)信息存放到目標(biāo)文件中,連接器(linker)會(huì)忽略該描述記錄。
- 如果compiler提供一個(gè)commentstring參數(shù),編譯程序?qū)⑸梢粋€(gè)警告。
#pragma comment( compiler )
exestr
- 將commentstring存放到目標(biāo)文件中去,并且在連接時(shí)存放到可執(zhí)行文件去中。
- 可執(zhí)行文件運(yùn)行后不會(huì)加載該字符串到內(nèi)存,但是該字符串可以被能夠在文件中搜索可打印字符串的程序檢索到。
- 該描述記錄的一個(gè)用處是在可執(zhí)行文件中嵌入版本號(hào)或者類似的信息。
#pragma comment( exestr, "test" )
lib
- 將靜態(tài)鏈接庫信息放置到目標(biāo)文件中去。
- 該描述類型必須有commentstring參數(shù),其中包含靜態(tài)鏈接庫的名稱或路徑。
- 該庫名放在目標(biāo)文件中默認(rèn)庫搜索信息之后,linker會(huì)像在命令行中輸入一樣搜索該庫名。
- 可以在一個(gè)源文件中放置多個(gè)庫搜索信息,并且按照它們出現(xiàn)在源文件中的順序存放在目標(biāo)文件中。
#pragma comment( lib, "emapi" )
linker
- 在目標(biāo)文件中放置連接程序選項(xiàng)。
- 可以用它指定連接程序選項(xiàng)來代替在Project Setting對(duì)話框中Link頁內(nèi)的選項(xiàng)。例如,你可以指定/include選項(xiàng)以強(qiáng)制包含一個(gè)符號(hào):
#pragma comment(linker, "/include:__mySymbol") - 只有下列選項(xiàng)可用于linker標(biāo)識(shí)符
/DEFAULTLIB
/EXPORT
/INCLUDE
/MANIFESTDEPENDENCY
/MERGE
/SECTION
user
- 將普通描述信息存放在目標(biāo)文件中。commentstring參數(shù)包含描述的文本。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 )
作用
控制對(duì)源文件中瀏覽信息或依賴信息的收集。
備注
瀏覽器(Browser)
- 可以打開或關(guān)閉收集,也可以指定在收集信息時(shí)忽略特定名稱或類型。
- 若要打開瀏覽信息的收集,必須先啟用瀏覽信息功能。
- references選項(xiàng)可以有也可以沒有name參數(shù)。使用沒有name參數(shù)的references選項(xiàng)將打開或者關(guān)閉引用信息的收集(然而繼續(xù)收集其它瀏覽信息)。
- 使用有name和off參數(shù)的references選項(xiàng)將阻止從瀏覽信息窗口中出現(xiàn)引用到的名字。用這個(gè)語法將忽略你不感興趣的名字和類型從而減少瀏覽信息文件的大小。
- 若要防止預(yù)處理器擴(kuò)展name(如將NULL擴(kuò)展到0),請(qǐng)使用引號(hào)將其包含。
// 關(guān)閉收集瀏覽信息
#pragma component(browser, off)
// 關(guān)閉收集引用信息
#pragma component(browser, off, references)
// 關(guān)閉收集DWORD類型的引用信息
#pragma component(browser, off, references, DWORD)
// 打開收集DWORD類型的引用信息
#pragma component(browser, on, references, DWORD)
// 防止預(yù)處理器擴(kuò)展name(如:NULL擴(kuò)展為0)
#pragma component(browser, off, references, "NULL")
最小化重建(Minimal Rebuild)
- Visual C++的最小化重建功能要求編譯器創(chuàng)建并保存需要大量磁盤空間的C++類依賴信息。
- 使用
#pragma component(minrebuild,off)關(guān)閉信息收集。 - 使用
#pragma component(minrebuild,on)重新打開依賴信息。
減少類型信息(Reduce Type Information)
- mintypeinfo選項(xiàng)將減少指定區(qū)域的調(diào)試信息。此信息的量相當(dāng)大,會(huì)影響.pdb和.obj文件。
- 不能在mintypeinfo區(qū)域中調(diào)試類和結(jié)構(gòu)。
- 使用mintypeinfo選項(xiàng)可幫助避免以下警告:
LINK : warning LNK4018: too many type indexes in PDB "filename", discarding subsequent type information
更多詳細(xì)內(nèi)容,請(qǐng)參閱Enable Minimal Rebuild(/Gm)編譯程序選項(xiàng)。
conform
語法
#pragma conform(name [, show ] [, on | off ] [ [, push | pop ] [, identifier ] ] )
作用
指定/Zc:forScope編譯器選項(xiàng)的運(yùn)行時(shí)行為。
備注
- name:指定要修改的編譯器選項(xiàng)的名稱。 唯一有效的name為forScope。
- show(可選): 將name的當(dāng)前設(shè)置(true或false)在編譯期間以警告消息的方式顯示。
- on、off(可選): 將name設(shè)置為on將啟用/Zc:forScope編譯器選項(xiàng)。默認(rèn)值為off。
- push(可選): 將name的當(dāng)前值推送到內(nèi)部編譯器堆棧。如果指定identifier,則可為要推送到堆棧的name指定on或off值。
- pop(可選):將name的值設(shè)置為位于內(nèi)部編譯器堆棧頂部的值,然后彈出堆棧。 如果使用pop指定 identifier,則堆棧將彈回,直到它找到具有identifier的記錄(也會(huì)彈出);堆棧上的下一記錄中的 name的當(dāng)前值將變?yōu)閚ame的新值。如果使用不在堆棧上的記錄中的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變量的默認(rèn)段為
.rdata。 - 某些const變量(如標(biāo)量)會(huì)自動(dòng)內(nèi)斂到代碼流中。
- 內(nèi)斂代碼將不會(huì)存放在
.rdata中。 - 在const_seg中定義需要?jiǎng)討B(tài)初始化的對(duì)象會(huì)導(dǎo)致未定義的行為。
- 不帶參數(shù)的#pragma const_seg會(huì)將段重置為
.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文件中存放初始化變量的數(shù)據(jù)段。
備注
- obj文件中存放初始化變量的默認(rèn)段為
.data。 - 未初始化的變量被視為初始化為零并且存儲(chǔ)在
.bss中。 - 不帶參數(shù)的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, ...] )
作用
指示函數(shù)、類型或任何其他標(biāo)識(shí)符已廢棄。
備注
- 當(dāng)編譯器遇到廢棄的符號(hào)時(shí),會(huì)拋出警告C4995。
- 可以廢棄宏名稱。但是需要將宏名稱包含在引號(hào)內(nèi),否則宏將展開。
- 可以使用deprecated __declspec廢棄重載函數(shù)。
示例代碼
// 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"))
作用
將記錄放在一個(gè)對(duì)象中。 鏈接器將檢查這些記錄中的潛在不匹配項(xiàng)。
備注
- link項(xiàng)目時(shí),如果項(xiàng)目包含name相同但value不同的兩個(gè)對(duì)象,則linker將拋出錯(cuò)誤LNK2038。
- 使用detect_mismatch可防止連接中存在不一致的對(duì)象文件。
- 名稱和值都是字符串,遵循轉(zhuǎn)義字符和連接的字符串規(guī)則。 同時(shí)區(qū)分大小寫,不能包含逗號(hào)、等號(hào)、引號(hào)或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 編譯這兩個(gè)文件,則會(huì)收到錯(cuò)誤 LNK2038。
execution_character_set
語法
#pragma execution_character_set("target")
作用
指定運(yùn)行時(shí)字符集。
備注
- target指定字符集名稱,目前只支持“utf-8”;
- 該指令在Visual Studio 2015 Updatae 2中廢棄了。建議使用編譯選項(xiàng)/execution-charset:utf-8或/utf-8配合u8前綴來指定字符集。
- 默認(rèn)情況下編譯器采用系統(tǒng)當(dāng)前字符集對(duì)源文件進(jìn)行編碼。
- 在未指定編碼集的情況下在不同的電腦上進(jìn)行編譯可能會(huì)引起編譯警告和錯(cuò)誤。
示例代碼
#pragma execution_character_set("utf-8")
fenv_access
語法
#pragma fenv_access [ON | OFF]
作用
禁用(ON)或啟用(OFF)可能更改標(biāo)記測試和模式更改的優(yōu)化。
備注
- 默認(rèn)情況下,fenv_access為OFF。
- 有關(guān)浮點(diǎn)行為的詳細(xì)信息,請(qǐng)參閱/fp(指定浮點(diǎn)行為)
- fenv_access的優(yōu)化類型有如下幾種。
a. 全局公共子表達(dá)式消除
b. 代碼移動(dòng)
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)之后,由于編譯器執(zhí)行編譯時(shí)計(jì)算,這個(gè)過程未使用控制模式,因此輸出不同。
out=1.000000000000000e-002
float_control
語法
float_control( value,setting [push] | push | pop )
作用
指定函數(shù)的浮點(diǎn)行為。
備注
- value: 可以是precise或except。
- setting: 可以是on或off。
- 如果vaule是precise,則設(shè)置precise和except為setting的值。
- except只有在precise為on的情況下設(shè)置為on。
- push: 將當(dāng)前的float_control設(shè)置壓入編譯器內(nèi)部堆棧。
- pop: 從內(nèi)部編譯器堆棧頂部移除float_control設(shè)置,使其成為新的float_control設(shè)置。
- 當(dāng)except打開時(shí),無法關(guān)閉float_control precise。同樣,當(dāng)fevn_access打開時(shí),無法關(guān)閉precise。
示例代碼
// 從嚴(yán)格模式轉(zhuǎn)換到快速模式
#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)
// 從快速模式轉(zhuǎn)換到嚴(yán)格模式
#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)
// 捕獲浮點(diǎn)數(shù)溢出異常
// 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]
作用
決定是否使用浮點(diǎn)數(shù)縮寫形式。
備注
- 默認(rèn)情況下,fp_contract處于打開狀態(tài)。
示例代碼
// 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, ...] )
作用
指定對(duì)function參數(shù)列表中的函數(shù)進(jìn)行顯示函數(shù)調(diào)用。
備注
- 如果使用intrinsic編譯指令(或/Oi編譯選項(xiàng))指示編譯生成內(nèi)斂函數(shù)(內(nèi)斂函數(shù)如同嵌入代碼一樣生成,不作為一個(gè)函數(shù)調(diào)用),則可以使用function編譯指令來實(shí)現(xiàn)顯示函數(shù)調(diào)用。
- 一旦設(shè)定了function編譯指令,它將在包含指定函數(shù)的第一個(gè)函數(shù)定義中生效。該效果持續(xù)到源文件結(jié)尾或遇到instrsic編譯指令指定相同的函數(shù)。
- function僅能在函數(shù)的外部使用- 全局級(jí)別。
示例代碼
// 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" )]
作用
提供對(duì)預(yù)編譯文件名和編譯狀態(tài)的保存位置的額外控制。
備注
- filename是要使用或創(chuàng)建的預(yù)編譯頭文件的名稱(取決于是否指定/Yu或/Yc)。
- 如果filename不包含路徑說明,則假定預(yù)編譯頭文件與源文件處于同一目錄中。
- 當(dāng)用/Yc編譯時(shí),如果C或C++文件中包含了一個(gè)hdrstop編譯指令,則編譯器保存編譯指令之前的編譯狀態(tài)。編譯指令之后的編譯狀態(tài)不被保存。
- filename指定了預(yù)編譯狀態(tài)保存的文件名。文件名是字符串,因此受C/C++的字符串約束。必須通過引號(hào)同時(shí)使用轉(zhuǎn)義符(反斜杠)來指定目錄名稱。例如:
#pragma hdrstop( "c:\\projects\\include\\myinc.pch" ) - 預(yù)編譯頭文件的名稱根據(jù)以下規(guī)則按優(yōu)先順序決定:
a. /Fp編譯選項(xiàng)的參數(shù)
b. #pragma hdrstop 的 filename 參數(shù)
c. 擴(kuò)展名為.PCH的源文件基名稱 - 如果/Yc和/Yu兩個(gè)編譯選項(xiàng)以及hdrstop編譯指令都未指定文件名,則將源文件的基名稱用作預(yù)編譯頭文件的名稱。
- 可以使用預(yù)處理命令來執(zhí)行宏替換,如下所示:
#define INCLUDE_PATH "c:\\progra~`1\\devstsu~1\\vc\\include\\"
#define PCH_FNAME "PROG.PCH"
.
.
.
#pragma hdrstop( INCLUDE_PATH PCH_FNAME )
- hdrstop必須出現(xiàn)在任何數(shù)據(jù)或函數(shù)聲明/定義的外部。
- 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的別名。
備注
- 有些文件系統(tǒng)允許長度超過8.3 FAT文件系統(tǒng)限制的文件名。因?yàn)檩^長的頭文件名的前8個(gè)字符可能不是唯一的,因此編譯器不能簡單的將較長的名稱截?cái)酁?.3。
- 當(dāng)編譯器遇到long_filename字符串是,都將用short_filename進(jìn)行替換,并改為查找shot_filename頭文件。
- 該指令必須在#include指令之前出現(xiàn)。
// 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"
- 別名必須要完全符合規(guī)范,包括大小寫,拼寫,雙引號(hào)和尖括號(hào)的使用。
- include_alias對(duì)文件名進(jìn)行簡單的字符串匹配,不會(huì)對(duì)文件名進(jìn)行其它校驗(yàn)。下面代碼將不執(zhí)行別名替換操作。
#pragma include_alias("mymath.h", "math.h")
#include "./mymath.h"
#include "sys/mymath.h"
- include_alias不會(huì)替換/Yu和/Yc以及hdrstop指令參數(shù)的頭文件名。
// 編譯選項(xiàng)
/YcAppleSystemHeaderStop.h
// 頭文件包含
#include <AppleSystemHeaderStop.h>
- 可以使用include_alias將任何頭文件名映射到另一個(gè)頭文件名。
#pragma include_alias( "api.h", "c:\version1.0\api.h" )
#pragma include_alias( <stdio.h>, <newstdio.h> )
#include "api.h"
#include <stdio.h>
- 不要將用雙引號(hào)括起來的文件名與用尖括號(hào)括起的文件名混淆使用。例如,給定上述兩個(gè)#pragma include_alias指令,編譯器將不對(duì)下列 #include 指令執(zhí)行任何替換:
#include <api.h>
#include "stdio.h"
- 以下指令將報(bào)錯(cuò)。
#pragma include_alias(<header.h>, "header.h") // Error
- 錯(cuò)誤消息中提示的文件名(或作為預(yù)定義的 FILE 宏的值的文件名)是文件在執(zhí)行替換后的名稱。
#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]} )
作用
指定影響啟動(dòng)代碼的執(zhí)行順序的關(guān)鍵字或代碼段。
備注
- 本節(jié)中segment和section的含義是可互換的。
- 由于全局靜態(tài)對(duì)象的初始化可能涉及代碼執(zhí)行,因此,必須指定一個(gè)關(guān)鍵字用于確定對(duì)象的構(gòu)造時(shí)間。
- init_seg在動(dòng)態(tài)鏈接庫(DLL)或需要初始化的庫中使用特別重要。
- init_seg的選項(xiàng)如下:
a. compiler: 保留給Microsoft C運(yùn)行庫初始化使用,該組中的對(duì)象會(huì)優(yōu)先構(gòu)造。
b. lib: 用于第三方類庫的初始化,該組中的對(duì)象在compiler之后構(gòu)造。
c. user: 可供任何用戶使用,該組中的對(duì)象最后構(gòu)造。
d. section-name: 顯式指定初始化的段名。該段中的對(duì)象通過顯式構(gòu)造函數(shù)構(gòu)造,并且對(duì)象地址存放在該段中。該段中存放著用于初始化該段之后的全局變量的輔助函數(shù)指針。
e. func-name: 指定程序退出時(shí)替換atexit的函數(shù)。在自己的退出函數(shù)中可以控制不同模塊的析構(gòu)順序。這個(gè)函數(shù)必須具有和atexit函數(shù)相同的形式:
int funcname(void (__cdecl *)(void)); - 可以通過顯式指定段名稱來延遲對(duì)象的初始化,但是必須為每個(gè)靜態(tài)對(duì)象顯式調(diào)用構(gòu)造函數(shù)進(jìn)行初始化。
- func-name不需要用引號(hào)包含。
- 各個(gè)對(duì)象存放在由xxx_seg編譯指令指定的段中。
- 默認(rèn)情況下,init_seg部分是只讀的,如果段名稱是
.CRT,則編譯器會(huì)自動(dòng)將段屬性修改為只讀,即使段標(biāo)志為讀寫。 - 每個(gè)源文件中只能出現(xiàn)一次init_seg。
- 模塊中聲明的對(duì)象不會(huì)由C運(yùn)行時(shí)自動(dòng)初始化,需要我們主動(dòng)調(diào)用。如果對(duì)象沒有用戶定義的構(gòu)造函數(shù),系統(tǒng)會(huì)生成默認(rèn)構(gòu)造函數(shù),但是還是需要我們主動(dòng)調(diào)用。
示例代碼
// 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()
剩余編譯指令內(nèi)容請(qǐng)閱讀#pragma編譯指令大全(下)