pragma詳解

 #pragma詳解(一)
在#Pragma是預處理指令它的作用是設定編譯器的狀態或者是指示編譯器完成一些特定的動作。#pragma指令對每個編譯器給出了一個方法,在保持與C和C ++語言完全兼容的情況下,給出主機或操作系統專有的特徵。依據定義,編譯指示是機器或操作系統專有的,且對於每個編譯器都是不同的。其格式一般爲: #Pragma Para
其中Para 爲參數,下面來看一些常用的參數。
(1)message 參數。 Message 參數是我最喜歡的一個參數,它能夠在編譯信息輸出窗口中輸出相應的信息,這對於源代碼信息的控制是非常重要的。其使用方法爲:
#Pragma message(“消息文本”)
當編譯器遇到這條指令時就在編譯輸出窗口中將消息文本打印出來。當我們在程序中定義了許多宏來控制源代碼版本的時候,我們自己有可能都會忘記有沒有正確的設置這些宏,此時我們可以用這條指令在編譯的時候就進行檢查。假設我們希望判斷自己有沒有在源代碼的什麼地方定義了_X86這個宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
當我們定義了_X86這個宏以後,應用程序在編譯時就會在編譯輸出窗口裏顯示“_X86 macro activated!”。我們就不會因爲不記得自己定義的一些特定的宏而抓耳撓腮了。
(2)另一個使用得比較多的pragma參數是code_seg。格式如:
#pragma code_seg( [\section-name\[,\section-class\] ] )
它能夠設置程序中函數代碼存放的代碼段,使用沒有section-name字符串的#pragmacode_seg可在編譯開始時將其復位,當我們開發驅動程序的時候就會使用到它。
(3)#pragma once (比較常用)
只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,這條指令實際上在VC6中就已經有了,但是考慮到兼容性並沒有太多的使用它。
(4)#pragma hdrstop表示預編譯頭文件到此爲止,後面的頭文件不進行預編譯。BCB可以預編譯頭文件以加快鏈接的速度,但如果所有頭文件都進行預編譯又可能佔太多磁盤空間,所以使用這個選項排除一些頭文件。有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A編譯。你可以用#pragma startup指定編譯優先級,如果使用了#pragma package(smart_init) ,BCB就會根據優先級的大小先後編譯。
(5)#pragma resource \*.dfm\表示把*.dfm文件中的資源加入工程。*.dfm中包括窗體外觀的定義。
(6)#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 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這裏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)。
(7)#pragma comment(...)該指令將一個註釋記錄放入一個對象文件或可執行文件中。常用的lib關鍵字,可以幫我們連入一個庫文件。
(8)·通過#pragma pack(n)改變C編譯器的字節對齊方式。
在C語言中,結構是一種複合數據類型,其構成元素既可以是基本數據類型(如int、long、float等)的變量,也可以是一些複合數據類型(如數組、結構、聯合等)的數據單元。在結構中,編譯器爲結構的每個成員按其自然對界(alignment)條件分配空間。各個成員按照它們被聲明的順序在內存中順序存儲,第一個成員的地址和整個結構的地址相同。
例如,下面的結構各成員空間分配情況:
struct test
{
char x1;
short x2;
float x3;
char x4;
};
結構的第一個成員x1,其偏移地址爲0,佔據了第1個字節。第二個成員x2爲short類型,其起始地址必須2字節對界,因此,編譯器在x2和x1之間填充了一個空字節。結構的第三個成員x3和第四個成員x4恰好落在其自然對界地址上,在它們前面不需要額外的填充字節。在test結構中,成員x3要求4字節對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件爲4字節,編譯器在成員x4後面填充了3個空字節。整個結構所佔據空間爲12字節。更改C編譯器的缺省字節對齊方式。
在缺省情況下,C編譯器爲每一個變量或是數據單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變缺省的對界條件:
· 使用僞指令#pragma pack (n),C編譯器將按照n個字節對齊。
· 使用僞指令#pragma pack (),取消自定義字節對齊方式。
另外,還有如下的一種方式:
· __attribute((aligned (n))),讓所作用的結構成員對齊在n字節自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。
· __attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用字節數進行對齊。
以上的n = 1, 2, 4, 8, 16... 第一種方式較爲常見。
應用實例
在網絡協議編程中,經常會處理不同協議的數據報文。一種方法是通過指針偏移的方法來得到各種信息,但這樣做不僅編程複雜,而且一旦協議有變化,程序修改起來也比較麻煩。在瞭解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來獲取各種信息。這樣做,不僅簡化了編程,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程序無需修改,省時省力。下面以TCP協議首部爲例,說明如何定義協議結構。
其協議結構定義如下:
#pragma pack(1) // 按照1字節方式進行對齊
struct TCPHEADER
{
short SrcPort; // 16位源端口號
short DstPort; // 16位目的端口號
int SerialNo; // 32位序列號
int AckNo; // 32位確認號
unsigned char HaderLen : 4; // 4位首部長度
unsigned char Reserved1 : 4; // 保留6位中的4位
unsigned char Reserved2 : 2; // 保留6位中的2位
unsigned char URG : 1;
unsigned char ACK : 1;
unsigned char PSH : 1;
unsigned char RST : 1;
unsigned char SYN : 1;
unsigned char FIN : 1;
short WindowSize; // 16位窗口大小
short TcpChkSum; // 16位TCP檢驗和
short UrgentPointer; // 16位緊急指針
};
#pragma pack() // 取消1字節對齊方式
指定連接要使用的庫
比如我們連接的時候用到了 WSock32.lib,你當然可以不辭辛苦地把它加入到你的工程中。但是我覺得更方便的方法是使用 #pragma 指示符,指定要連接的庫:
#pragma comment(lib, "WSock32.lib")
#pragma詳解(二)
每種C和C++的實現支持對其宿主機或操作系統唯一的功能。例如,一些程序需要精確控制超出數據所在的儲存空間,或着控制特定函數接受參數的方式。#pragma指示使每個編譯程序在保留C和C++語言的整體兼容性時提供不同機器和操作系統特定的功能。編譯指示被定義爲機器或操作系統特定的,並且通常每種編譯程序是不同的。
語法:
#pragma token_string
“token_string”是一系列字符用來給出所需的特定編譯程序指令和參數。數字符號“#”必須是包含編譯指令的行中第一個非空白字符;而空白字符可以隔開數字符號“#”和關鍵字“pragma”。在#pragma後面,寫任何翻譯程序能夠作爲預處理符號分析的文本。#pragma的參數類似於宏擴展。
如果編譯程序發現它不認得一個編譯指示,它將給出一個警告,可是編譯會繼續下去。
爲了提供新的預處理功能,或者爲編譯程序提供由實現定義的信息,編譯指示可以用在一個條件語句內。C和C++編譯程序可以識別下列編譯程序指令。
alloc_text
comment
init_seg*
optimize
auto_inline
component
inline_depth
pack
bss_seg
data_seg
inline_recursion
pointers_to_members*
check_stack
function
intrinsic
setlocale
code_seg
hdrstop
message
vtordisp*
const_seg
include_alias
once
warning
*僅用於C++編譯程序。
1 alloc_text
#pragma alloc_text( "textsection", function1, ... )
命名特別定義的函數駐留的代碼段。該編譯指示必須出現在函數說明符和函數定義之間。
alloc_text編譯指示不處理C++成員函數或重載函數。它僅能應用在以C連接方式說明的函數——就是說,函數是用extern "C"連接指示符說明的。如果你試圖將這個編譯指示應用於一個具有C++連接方式的函數時,將出現一個編譯程序錯誤。
由於不支持使用__based的函數地址,需要使用alloc_text編譯指示來指定段位置。由textsection指定的名字應該由雙引號括起來。
alloc_text編譯指示必須出現在任何需要指定的函數說明之後,以及這些函數的定義之前。
在alloc_text編譯指示中引用的函數必須和該編譯指示處於同一個模塊中。如果不這樣做,使以後一個未定義的函數被編譯到一個不同的代碼段時,錯誤會也可能不會被捕獲。即使程序一般會正常運行,但是函數不會分派到應該在的段。
alloc_text的其它限制如下:
它不能用在一個函數內部。
它必須用於函數說明以後,函數定義以前。
2 auto_inline
#pragma auto_inline( [{on | off}] )
當指定off時將任何一個可以被考慮爲作爲自動嵌入擴展候選的函數排除出該範圍。爲了使用auto_inline編譯指示,將其緊接着寫在一個函數定義之前或之後(不是在其內部)。該編譯指示將在其出現以後的第一個函數定義開始起作用。auto_inline編譯指示對顯式的inline函數不起作用。
3 bss_seg
#pragma data_seg( ["section-name"[, "section-class"] ] )
爲未初始化數據指定缺省段。data_seg編譯指示除了工作於已初始化數據而不是未初始化的以外具有一樣的效果。在一些情況下,你能使用bss_seg將所有未初始化數據安排在一個段中來加速你的裝載時間。
#pragma bss_seg( "MY_DATA" )
將導致把#pragma語句之後的未初始化的數據安排在一個叫做MY_DATA的段中。
用bss_seg編譯指示分配的數據不包含任何關於其位置的信息。
第二個參數section-class是用於兼容2.0版本以前的Visual C++的,現在將忽略它。
4 check_stack
#pragma check_stack([ {on | off}] )
#pragma check_stack{+ | –}
如果指定off(或者“-”)指示編譯程序關閉堆棧探測,或者指定on(或“+”)打開堆棧探測。如果沒有給出參數,堆棧探測將根據默認設置決定。該編譯指示將在出現該指示之後的第一個函數開始生效。堆棧探測既不是宏和能夠生成嵌入代碼函數的一部分。
如果你沒有給出check _stack編譯指示的參數,堆棧檢查將恢復到在命令行指定的行爲。詳細情況見編譯程序參考。#pragma check_stack和/Gs選項的互相作用情況在表2.1中說明。
表 2.1 使用check_stack編譯指示
編譯指示
用/Gs選項編譯?
行爲
#pragma check_stack()或#pragma check_stack

後續的函數關閉堆棧檢查
#pragma check_stack()或#pragma check_stack

後續的函數打開堆棧檢查
#pragma check_stack(on)或#pragma check_stack(+)
是或者否
後續的函數打開堆棧檢查
#pragma check_stack(off)或#pragma check_stack(-)
是或者否
後續的函數關閉堆棧檢查
5 code_seg
#pragma code_seg( ["section-name"[,"section-class"] ] )
指定分配函數的代碼段。code_seg編譯指示爲函數指定默認的段。你也能夠像段名一樣指定一個可選的類名。使用沒有段名字符串的#pragma code_seg將恢復分配到編譯開始時候的狀態。
6 const_seg
#pragma const_seg( ["section-name"[, "section-class"] ] )
指定用於常量數據的默認段。data_seg編譯指示除了可以工作於所有數據以外具有一樣的效果。你能夠使用該編譯指示將你的常量數據保存在一個只讀的段中。
#pragma const_seg( "MY_DATA" )
導致在#pragma語句後面的常量數據分配在一個叫做MY_DATA的段中。
用const_seg編譯指示分配的數據不包含任何關於其位置的信息。
第二個參數section-class是用於兼容2.0版本以前的Visual C++的,現在將忽略它。
7 comment
#pragma comment( comment-type [, commentstring] )
將描述記錄安排到目標文件或可執行文件中去。comment-type是下面說明的五個預定義標識符中的一個,用來指定描述記錄的類型。可選的commentstring是一個字符串文字值用於爲一些描述類型提供附加的信息。因爲commentstring是一個字符串文字值,所以它遵從字符串文字值的所有規則,例如換碼字符、嵌入的引號(")和聯接。
7-1 compiler
在目標文件中放置編譯程序名和版本號。該描述記錄被連接程序忽略。如果你爲這個記錄類型提供一個commentstring參數,編譯程序將生成一個警告。
7-2 exestr
將commentstring放置到目標文件中去。在連結時,這個字符串再被放到可執行文件去中。當可執行文件被裝載時這個字符串不會被裝入內存,然而,它可以被一個能夠在文件中搜索可打印字符串的程序找到。該描述記錄的一個用處是在可執行文件中嵌入版本號或者類似的信息。
7-3 lib
將一個庫搜索記錄放置到目標文件中去。該描述類型必須有包含你要連接程序搜索的庫名(和可能的路徑)的commentstring參數。因爲在目標文件中該庫名先於默認的庫搜索記錄,所以連接程序將如同你在命令行輸入這些庫一樣來搜索它們。你可以在一個源文件中放置多個庫搜索記錄,每個記錄將按照它們出現在源文件中的順序出現在目標文件中。
7-4 linker
在目標文件中放置連接程序選項。你可以用這個描述類型指定連接程序選項來代替在Project Setting對話框中Link頁內的選項。例如,你可以指定/include選項以強迫包含一個符號:
#pragma comment(linker, "/include:__mySymbol")
7-5 user
在目標文件中包含一個普通描述記錄。commentstring參數包含描述的文本。該描述記錄將被連接程序忽略。
下面的編譯指示導致連接程序在連接時搜索EMAPI.LIB庫。連接程序首先在當前工作目錄然後在LIB環境變量指定的路徑中搜索。
#pragma comment( lib, "emapi" )
下面的編譯指示導致編譯程序將其名字和版本號放置到目標文件中去。
The following pragma causes the compiler to place the name and version number of the compiler in the object file:
#pragma comment( compiler )
注意,對於具有commentstring參數的描述記錄,你可以使用其它用作字符串文字量的宏來提供宏擴展爲字符串文字量。你也能夠聯結任何字符串文字量和宏的組合來擴展成爲一個字符串文字量。例如,下面的語句是可以接受的:
#pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ )
#pragma詳解(三)
component
#pragma component( browser, { on | off }[, references [, name ]] )
#pragma component( minrebuild, on | off )
從源文件內控制瀏覽信息和依賴信息的收集。
8-1 瀏覽信息(Browser)
你可以將收集打開或關閉,你也可以指定收集時忽略特別的名字。
使用on或off在編譯指示以後控制瀏覽信息的收集。例如:
#pragma component(browser, off)
終止編譯程序收集瀏覽信息。
注意,爲了用這個編譯指示打開瀏覽信息的收集,必須先從Project Setting對話框或者命令行允許瀏覽信息。
references選項可以有也可以沒有name參數。使用沒有name參數的references選項將打開或者關閉引用信息的收集(然而繼續收集其它瀏覽信息)。例如:
#pragma component(browser, off, references)
終止編譯程序收集引用信息。
使用有name和off參數的references選項將阻止從瀏覽信息窗口中出現引用到的名字。用這個語法將忽略你不感興趣的名字和類型從而減少瀏覽信息文件的大小。例如:
#pragma component(browser, off, references, DWORD)
從這一點以後忽略DWORD的引用。你能夠用on恢復DWORD的引用收集:
#pragma component(browser, on, references, DWORD)
這是唯一的方法可以恢復收集指定名字的引用,你必須顯式地打開任何你關閉的名字。
爲了防止預處理程序擴展名字(就像擴展NULL到0),用引號括起來:
#pragma component(browser, off, references, "NULL")
8-2 最小化重建(Minimal Rebuild)
Visual C++的最小化重建功能要求編譯程序創建並保存需要大量磁盤空間的C++類依賴信息。爲了節省磁盤空間,你能夠在你不需要收集依賴信息時使用#pragma component(minrebuild,off),例如,沒有改變過頭文件。在未修改過的類之後插入#pragma component(minrebuild,on)重新打開依賴信息。
詳見Enable Minimal Rebuild(/Gm)編譯程序選項。
9 data_seg
#pragma data_seg( ["section-name"[, "section-class"] ] )
指定數據的默認段。例如:
#pragma data_seg( "MY_DATA" )
導致在#pragma語句後分配的數據保存在一個叫做MY_DATA的段中。
用data_seg編譯指示分配的數據不包含任何關於其位置的信息。
第二個參數section-class是用於兼容2.0版本以前的Visual C++的,現在將忽略它。
10 function
#pragma function( function1 [, function2, ...] )
指定必須生成對編譯指示中參數列表內函數的調用。如果你使用intrinsic編譯指示(或者/Oi)來告訴編譯程序生成內含函數(內含函數如同嵌入代碼一樣生成,不作爲一個函數調用),你能夠用function編譯指示顯式地強迫函數調用。當遇到一個function編譯指示,它將在其後面遇到的第一個包含有內含函數的函數定義處生效。其持續作用到源文件的尾部或者出現對同一個內含函數指定intrinsic編譯指示。function編譯指示只能用於函數外——在全局層次。
爲了列出具有內含形式的函數表,參見#pragma intrinsic。
11 hdrstop
#pragma hdrstop [( "filename" )]
控制預編譯頭文件的工作方式。filename是要使用或者創建(依賴於是否指定了/Yu或/Yc)預編譯頭文件的名字。如果 filename不包括一個指定路徑,將假定預編譯頭文件和源文件處於同一個目錄中。當指定自動預編譯頭文件選項/YX時,所有指定的文件名將被忽略。
如果有/YX或者/Yc選項,而且C或C++文件包含了一個hdrstop編譯指示時,編譯程序保存編譯指示之前的編譯狀態。編譯指示之後的編譯狀態不被保存。
hdrstop編譯選項不能出現在一個頭文件內。它只能出現在源文件的文件級,它也不能出現在任何數據或者函數的說明或定義之中。
注意,除非指定沒有文件名的/YX選項或者/Yu或/Yc選項,否則hdrstop編譯指示將被忽略。
用一個文件名命名要保存編譯狀態的預編譯頭文件。在hdrstop和filename之間的空格是可選的。在hdrstop編譯指示中的文件名是一個字符串,這樣它服從於C或C++的字符串規則。特別的,你必須像下面例子裏面顯示的用引號括起來。
#pragma hdrstop( "c:\projects\include\myinc.pch" )
預編譯頭文件的文件名按照如下規則決定,按照優先次序:
/Fp編譯程序選項的參數;
由#pragma hdrstop的filename參數;
原文件名的基本文件名加上.PCH擴展名。
12 include_alias
#pragma include_alias( "long_filename", "short_filename" )
#pragma include_alias( <long_filename>, <short_filename> )
指定作爲long_filename別名的short_filename。一些文件系統允許超出8.3FAT文件系統限制的長頭文件名。編譯程序不能簡單地將長文件名截斷爲8.3名字,因爲長頭文件名的前8個字符可能不是唯一的。無論何時編譯程序遇到long_filename串,它代替short_filename,並且用short_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"
並不執行別名替代,因爲頭文件名字符串沒有精確匹配。另外,在/Yu,/Yc和/YX編譯程序選項,或hdrstop編譯指示中作爲參數的頭文件名不被替換。例如,如果你的源文件包含下列指示:
#include <AppleSystemHeaderStop.h>
相應的編譯程序選項必須是:
/YcAppleSystemHeaderStop.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"
文件VeryLongFileName.H產生下列錯誤信息:
myfile.h(15) : error C2059 : syntax error
還要注意的是不支持傳遞性。給出下面的指示:
#pragma include_alias( "one.h", "two.h" )
#pragma include_alias( "two.h", "three.h" )
#include "one.h"
編譯程序將搜索two.h而不是three.h。
13 init_seg
C++特有
#pragma init_seg({ compiler | lib | user | "section-name" [, "func-name"]} )
指定影響啓動代碼執行的關鍵字或代碼段。因爲全局靜態對象的初始化可以包含執行代碼,所以你必須指定一個關鍵字來定義什麼時候構造對象。在使用需要初始化的動態連接庫(DLL)或程序庫時使用init_seg編譯指示是尤其重要的。
init_seg編譯指示的選項有:
13-1 compiler
由Microsoft C運行時間庫保留。在這個組中的對象將第一個構造。
13-2 lib
用於第三方類庫開發者的初始化。在這個組中的對象將在標記爲構造compiler的對象之後,其它對象之前構造。
13-3 user
用於任何其它用戶。在這個組中的對象將最後構造。
13-4 section-name
允許顯式地指定初始化段。在用戶指定的section-name中的對象將不會隱式地構造,而它們
的地址將會被放置在由section-name命名的段中。
13-5 func-name
指定當程序退出時,作爲atexit函數調用的函數。這個函數必須具有和atexit函數相同的形式:
int funcname(void (__cdecl *)(void));
如果你需要延遲初始化,你能夠選擇指定顯式的段名。隨後你必須調用每個靜態對象的構造函數。
14 inline_depth
#pragma inline_depth( [0... 255] )
通過控制能夠被擴展的一系列函數調用(從0到255次)來控制嵌入函數擴展的發生次數,這個編譯指示控制用inline,__inline標記的或在/Ob2選項下能自動嵌入的嵌入函數。
inline_depth編譯指示控制能夠被擴展的一系列函數調用。例如,如果嵌入深度是4,並且如果A調用B然後調用C,所有的3次調用都將做嵌入擴展。然而,如果設置的最近一次嵌入深度是2,則只有A和B被擴展,而C仍然作爲函數調用。
爲了使用這個編譯指示,你必須設置編譯程序選項/Ob爲1或者2。用這個編譯指示指定的深度設定在該指示後面的第一個函數開始生效。如果你在括號內不指定一個值,inline_depth設置嵌入深度到默認值8。
在擴展時,嵌入深度可以被減少而不能被增加。如果嵌入深度是6,同時在擴展過程中預處理程序遇到一個inline_depth編譯指示設置爲8,則深度保持爲6。
嵌入深度0將拒絕嵌入擴展,深度255將設置在嵌入擴展時沒有限制。如果用一個沒有指定值的編譯指示,則使用爲默認值。
#pragma詳解(四)
inline_recursion
#pragma inline_recursion( [{on | off}] )
控制直接或者相互間的遞歸函數調用式的嵌入擴展。用這個編譯指示控制用inline,__inline標記的或在/Ob2選項下能自動嵌入的嵌入函數。使用這個編譯指示需要設置編譯程序選項/Ob爲1或者2。默認的inline_recursion狀態是off。這個編譯指示在出現該編譯指示之後第一個函數調用起作用,並不影響函數的定義。
inline_recursion編譯指示控制如何擴展遞歸函數。如果inline_recursion是off,並且如果一個嵌入函數調用了它自己(直接的或者間接的),函數將僅僅擴展一次。如果
inline_recursion是on,函數將擴展多次直到達到inline_depth的值或者容量限制。
16 intrinsic
#pragma intrinsic( function1 [, function2, ...] )
指定對在編譯指示參數表中函數調用是內含的。編譯程序像嵌入代碼一樣生成內含函數,而不是函數調用。下面列出了具有內含形式的庫函數。一旦遇到intrinsic編譯指示,它從第一個包含指定內含函數的函數定義開始起作用。作用持續到源文件尾部或者出現包含相同內含函數的function編譯指示。intrinsic編譯指示只能用在函數定義外——在全局層次。
下列函數具有內含形式:
_disable
_enable
_inp
_inpw
_lrotl
_lrotr
_outp
_outpw
_rotl
_rotr
_strset
abs
fabs
labs
memcmp
memcpy
memset
strcat
strcmp
strcpy
strlen
使用內含函數的程序更快,因爲它們沒有函數調用的額外代價,然而因爲有附加的代碼生成,可能比較大。
注意,_alloca和setjmp函數總是內含的,這個行爲不受intrinsic編譯指示影響。
下列浮點函數沒有內含形式。然而它們具有直接將參數通過浮點芯片傳送而不是推入程序堆棧的版本。
acos
asin
cosh
fmod
pow
sinh
tanh
當你同時指定/Oi和/Og編譯程序選項(或者任何包含/Og,/Ox,/O1和/O2的選項)時下列浮點函數具有真正的內含形式。
atan
exp
log10
sqrt
atan2
log
sin
tan
cos
你可以用編譯程序選項/Op或/Za來覆蓋真內含浮點選項的生成。在這種情況下,函數會像一般庫函數一樣被生成,同時直接將參數通過浮點芯片傳送而不是推入程序堆棧。
17 message
#pragma message( messagestring )
不中斷編譯,發送一個字符串文字量到標準輸出。message編譯指示的典型運用是在編譯時顯示信息。
下面的代碼段用message編譯指示在編譯過程中顯示一條信息:
#if _M_IX86 == 500
#pragma message( "Pentium processor build" )
#endif
messagestring參數可以是一個能夠擴展成字符串文字量的宏,並且你能夠用字符串文字量和宏的任何組合來構造。例如,下面的語句顯示被編譯文件的文件名和文件最後一次修改的日期和時間。
#pragma message( "Compiling " __FILE__ )
#pragma message( "Last modified on " __TIMESTAMP__ )
18 once
#pragma once
指定在創建過程中該編譯指示所在的文件僅僅被編譯程序包含(打開)一次。該編譯指示的一種常見用法如下:
//header.h
#pragma once
// Your C or C++ code would follow:
19 optimize
僅在專業版和企業版中存在
#pragma optimize( "[optimization-list]", {on | off} )
代碼優化僅有Visual C++專業版和企業版支持。詳見Visual C++ Edition。
指定在函數層次執行的優化。optimize編譯選項必須在函數外出現,並且在該編譯指示出現以後的第一個函數定義開始起作用。on和off參數打開或關閉在optimization-list指定的選項。
optimization-list能夠是0或更多個在表2.2中給出的參數:
表 2.2 optimize編譯指示的參數
參數
優化類型
a
假定沒有別名。
g
允許全局優化。
p
增強浮點一致性。
s 或 t
指定更短或者更快的機器代碼序列。
w
假定在函數調用中沒有別名。
y
在程序堆棧中生成框架指針。
這些和在/O編譯程序選項中使用的是相同的字母。例如:
#pragma optimize( "atp", on )
用空字符串("")的optimize編譯指示是一種特別形式。它要麼關閉所有的優化選項,要麼恢復它們到原始(或默認)的設定。
#pragma optimize( "", off )
#pragma optimize( "", on )
20 pack
#pragma pack( [ n] )
指定結構和聯合成員的緊縮對齊。儘管用/Zp選項設定整個翻譯單元的結構和聯合成員的緊
縮對齊,可以用pack編譯指示在數據說明層次設定緊縮對齊。從出現該編譯指示後的第一個結構或者聯合說明開始生效。這個編譯指示不影響定義。
當你使用#pragma pack(n),其中n是1,2,4,8或者16,第一個以後的每個結構成員保存在較小的成員類型或者n字節邊界上。如果你使用沒有參數的#pragma pack,結構成員將被緊縮到由/Zp指定的值。默認的/Zp緊縮的大小是/Zp8。
編譯程序還支持下面的增強語法:
#pragma pack( [ [ { push | pop}, ] [ identifier, ] ] [ n ] )
該語法允許你將使用不同緊縮編譯指示的組件合併到同一個翻譯單元內。
每次出現有push參數的pack編譯指示將保存當前的緊縮對齊值到一個內部的編譯程序堆棧。編譯指示的參數列表從左向右讀取。如果你使用了push,當前緊縮值被保存。如果你提供了一個n值,這個值將成爲新的緊縮值。如果你指定了一個你選定的標示符,這個標示符將和新的緊縮值關聯。
每次出現有pop參數的pack編譯指示從內部編譯程序堆棧頂部取出一個值並將那個值作爲新的緊縮對齊。如果你用了pop,而內部編譯程序堆棧是空的,對齊值將從命令行得到,同時給出一個警告。如果你用了pop並指定了n的值,那個值將成爲新的緊縮值。如果你用了pop並指定了一個標示符,將移去所有保存在堆棧中的的值直到匹配的找到匹配的標示符,和該標示符關聯的緊縮值也被從堆棧中移出來成爲新的緊縮值。如果沒有找到匹配的標示符,將從命令行獲取緊縮值併產生一個1級警告。默認的緊縮對齊是8。
pack編譯指示的新的增強功能允許你編寫頭文件保證在使用頭文件之前和其後的緊縮值是一樣的:
/* File name: include1.h
*/
#pragma pack( push, enter_include1 )
/* Your include-file code ... */
#pragma pack( pop, enter_include1 )
/* End of include1.h */
在前面的例子中,進入頭文件時將當前緊縮值和標示符enter_include1關聯並推入,被記住。在頭文件尾部的pack編譯選項移去所有在頭文件中可能遇到的緊縮值並移去和enter_include1關聯的緊縮值。這樣頭文件保證了在使用頭文件之前和其後的緊縮值是一樣的。
新功能也允許你在你的代碼內用pack編譯指示爲不同的代碼,例如頭文件設定不同的緊縮對齊。
#pragma pack( push, before_include1 )
#include "include1.h"
#pragma pack( pop, before_include1 )
在上一個例子中,你的代碼受到保護,防止了在include.h中的任何緊縮值的改變。
#pragma詳解(五)
pointers_to_members
C++特有
#pragma pointers_to_members(pointer-declaration, [most-general-representation] )
指定是否能夠在相關類定義之前說明一個指向類成員的指針,並且用於控制指針的大小和解釋指針的代碼。你能夠在你的源代碼中使用pointers_to_members編譯知識來代替/vmx編譯程序選項。
pointer-declaration參數指出是否在相關函數定義之前或其後你已經說明了一個指向成員的指針。pointer-declaration參數是下面兩個符號之一:
參數
說明
full_generality
生成安全的,但是有時不能優化的代碼。如果有一些指向成員的指針在相關類定義之前說明,你要用full_generality。這個參數總是使用由most-general-representation指定的指針表示方式。
best_case
對於所有指向成員的指針用最佳的表示方式生成安全的,優化的代碼。需要在說明一個指向類成員指針之前定義類。默認是best_case。
most-general-representaion參數指出在一個翻譯單元中編譯程序能夠安全引用任何指向類成員指針的最小指針表示方式。這個參數可以是下列之一:
參數
說明
single_inheritance
最普通的表示方式是單繼承,指向成員函數。如果用於指向具有多重或者虛擬繼承方式類成員的指針,將產生一個錯誤。
multi_inheritance
最普通的表示方式是多重繼承,指向成員函數。如果用於指向具有虛擬繼承方式類成員的指針,將產生一個錯誤。
virtual_inheritance
最普通的表示方式是虛擬繼承,指向成員函數。不會產生錯誤。當使用#pragma pointers_to_members (full_generality)時這是默認的參數。
22 setlocale
#pragma setlocale( "locale-string" )
定義用於翻譯寬字符常數和字符串文字量時用的地區(國家和語言)。由於用於從多字節字符轉換到寬字符的算法根據地區或者由於在運行可執行程序不同的地方進行編譯而不同,這個編譯指示提供一種在編譯時指定目標地區的方式。這保證寬字符字符串將以正確的格式保存。默認的locale-string是“C”。“C”地區將字符串中的每個字符作爲wchar_t(即unsigned int)映射其值。
23 vtordisp
C++特有
#pragma vtordisp({on | off} )
允許隱藏的附加vtordisp構造函數/析構函數替換成員。vtordisp編譯指示僅能夠用於具有虛擬基類的代碼。如果派生類從一個虛擬基類重載了一個虛擬函數,並且如果派生類的構造函數或析構函數用指向虛擬基類的指針調用了這個函數,編譯程序將根據虛擬基類在類中引入一個附加的隱藏“vtordisp”域。
vtodisp編譯選項影響它後面的類佈局。/vd0和/vd1選項爲整個模塊指定了相同的行爲。指定off將禁止隱藏的vtordisp成員,指定on(默認)將在它們需要的時候允許vtordisp。僅在不可能出現類的構造函數和析構函數通過this指針調用其指向對象中的虛擬函數時才關閉vtordisp。
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )
24 warning
#pragma warning( warning-specifier : warning-number-list [,warning-specifier : warning-number-list...] )
#pragma warning( push[ , n ] )
#pragma warning( pop )
允許有選擇地修改編譯程序警告信息的行爲。
warning-specifier能夠是下列值之一:
warning-specifier
含義
once
只顯示指定信息一次。
default
對指定信息應用默認的編譯程序選項。
1,2,3,4
對指定信息引用給定的警告等級。
disable
不顯示指定信息。
error
對指定信息作爲錯誤顯示。
warning-number_list能夠包含任何警告編號。如下,在一個編譯指示中可以指定多個選項:
#pragma warning( disable : 4507 34; once : 4385; error : 164 )
這等價於:
#pragma warning( disable : 4507 34 ) // Disable warning messages
// 4507 and 34.
#pragma warning( once : 4385 ) // Issue warning 4385
// only once.
#pragma warning( error : 164 ) // Report warning 164
// as an error.
對於那些關於代碼生成的,大於4699的警告標號,warning編譯指示僅在函數定義外時有效。如果指定的警告編號大於4699並且用於函數內時被忽略。下面例子說明了用warning編譯指示禁止、然後恢復有關代碼生成警告信息的正確位置:
int a;
#pragma warning( disable : 4705 )
void func()
{
a;
}
#pragma warning( default : 4705 )
warning編譯指示也支持下面語法:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
這裏n表示警告等級(1到4)。
warning(push)編譯指示保存所有警告的當前警告狀態。warning(push,n)保存所有警告的當前狀態並將全局警告等級設置爲n。
warning(pop)彈出最後一次推入堆棧中的警告狀態。任何在push和pop之間改變的警告狀態將被取消。考慮下面的例子:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
// Some code
#pragma warning( pop )
在這些代碼的結束,pop恢復了所有警告的狀態(包括4705,4706和4707)到代碼開始時候的樣子。
當你編寫頭文件時,你能用push和pop來保證任何用戶修改的警告狀態不會影響正常編譯你的頭文件。在頭文件開始的地方使用push,在結束地方使用pop。例如,假定你有一個不能順利在4級警告下編譯的頭文件,下面的代碼改變警告等級到3,然後在頭文件的結束時恢復到原來的警告等級。
#pragma warning( push, 3 )
// Declarations/ definitions
#pragma warning( pop )
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章