3    プラグマ・プリプロセッサ指示文

#pragma 指示文は,コンパイラごとに異なる機能をインプリメントするための標準的な方法です。 この章では,C コンパイラでサポートされている処理系固有のプラグマについて説明します。

Compaq C のすべてのインプリメンテーションでサポートされているプラグマについては,『Compaq C 言語リファレンス・マニュアル』に記載があります。

プラグマの中には,デフォルトでマクロ展開を実行するものがあります。 『Compaq C 言語リファレンス・マニュアル』にそのようなプラグマの一覧があります。 プラグマ名やキーワードと同じ名前のマクロを定義しているプログラムを移植する際に,マクロ展開を避けるため,接頭語としてプラグマ名に下線を 2 つ付ける方法についても説明しています。

プラグマ名に接尾語の _m を付加すると,どのプラグマに対してもマクロ展開を強制することができます。 プラグマ名の後に _m がある場合,プラグマ名の後のテキストはマクロ置換であると見なされます。 3.1.3 項 の例を参照してください。

3.1    #pragma assert 指示文

#pragma  assert 指示文を使用すると,コンパイラがより効率的なコードを生成するために使用できる,プログラムに関するアサーションを指定できます。

#pragma  assert 指示文は,プログラムを正しく動作させるために必要なものではありません。 ただし,#pragma  assert を指定する場合は,指定するアサーションが正しくないと,プログラムが誤動作するおそれがあります。

#pragma  assert 指示文の形式は次のとおりです。

#pragma  assert func_attrs(identifier-list)function-assertions

#pragma  assert global_status_variable(variable-list)

#pragma  assert non_zero(constant-expression) "string-literal"

3.1.1    #pragma assert func_attrs

この形式の #pragma assert 指示文を使用すると,関数の属性にアサーションが設定されます。

この形式のプラグマの構文は,次のとおりです。

#pragma  assert func_attrs(identifier-list)function-assertions

identifier-list

関数識別子のリスト。 コンパイラは,function-assertions に基づいて,これらの関数に仮定条件を設定できます。 複数の識別子を指定する場合は,コンマで区切ります。

function-assertions

関数に対してコンパイラが仮定条件を設定するために使用するアサーションのリスト。 以下のアサーションの中から 1 つ以上指定し,複数指定する場合はホワイト・スペースで区切ります。

noreturn

コンパイラは,ルーチンを呼び出した後,ルーチンから戻らないものと仮定することができます。

nocalls_back

コンパイラは,この関数から制御を戻すまでは,そのソース・モジュール内のルーチンが呼び出されないと仮定することができます。

nostate

コンパイラは,関数のリターン値が関数の引数のみによって決まり,この関数には副作用がないと仮定することができます。 関数に対して noeffects と nostate の両方がマークされると,コンパイラは,この関数への重複する呼び出しを削除することができます。

noeffects

コンパイラは,この関数には,関数のリターン値の設定以外の効果がないと仮定することができます。 コンパイラは,関数呼び出しからのリターン値が一度も使用されないと判断した場合,その呼び出しを削除することができます。

file_scope_vars(option)

コンパイラは,ファイル範囲で (内部または外部リンケージで) 宣言された変数に関数がアクセスする方法についての仮定条件を設定できます。 option は,以下のキーワードのいずれかになります。

none

関数は,型が volatile でなく,また #pragma assert global_status_variable にリストされていないファイル範囲の変数に対して,読み取りも書き込みも行いません。

noreads

関数は,型が volatile でなく,また #pragma assert global_status_variable にリストされていないファイル範囲の変数に対して,読み取りを行いません。

nowrites

関数は,型が volatile でなく,また #pragma assert global_status_variable にリストされていないファイル範囲の変数に対して,書き込みを行いません。

format (style, format-index, first-to-check-index)

コンパイラは,フォーマット文字列に対して型検査される printf または scanf スタイルの引数をこの関数が取ると仮定することができます。

フォーマット属性により,引数としてフォーマット文字列を取る独自の関数を指定して,コンパイラにこれらの関数への呼び出しのエラーをチェックさせることができます。 コンパイラは,ライブラリ関数 printffprintfsprintfsnprintfscanffscanfsscanf に対して,intrinsic が有効になっている場合 (省略時) に,フォーマットをチェックします。 フォーマット属性を使用すると,これらの関数に対して intrinsic が有効でない場合に,フォーマットがチェックされるようにすることができます。

style

フォーマット文字列の解釈方法を指定します。printfscanf のいずれかを指定します。

format-index

どの引数がフォーマット文字列引数かを指定します (1 から始まる)。

first-to-check-index

フォーマット文字列に対してチェックを行う,最初の引数の番号を指定します。

チェックする引数がない場合 (vprintf など) は,3 番目のパラメータにゼロを指定します。 この場合,コンパイラはフォーマット文字列に矛盾がないことだけをチェックします。 たとえば,次のように宣言すると,コンパイラは your_printf の呼び出しの引数について,printf スタイルのフォーマット文字列引数 your_format との整合性をチェックします。

extern int
your_printf (void *your_object, const char *your_format, ...);
#pragma assert func_attrs(your_printf) format (printf, 2, 3)
 
 

フォーマット文字列 (your_format) は,関数 your_printf の 2 番目の引数であり,チェックする引数は 3 番目の引数から始まるので,フォーマット属性の正しいパラメータは,2 と 3 です。

この形式の #pragma assert 指示文は,ファイル範囲に記述しなければなりません。

identifier-list 内の識別子は,#pragma assert 指示文の位置から見えるところで宣言されていなければなりません。

#pragma assert func_attrs 指示文で異なるアサーションを指定する限りは,複数の指示文に同じ関数を記述できます。 たとえば,次の記述は有効です。

#pragma assert func_attrs(a) nocalls_back 
#pragma assert func_attrs(a) file_scope_vars(noreads)

次の記述は無効です。

#pragma assert func_attrs(a) file_scope_vars(noreads)
#pragma assert func_attrs(a) file_scope_vars(nowrites)

3.1.2    #pragma assert global_status_variable

この形式の #pragma assert 指示文を使用すると,グローバル状態変数とみなされる変数を指定できます。 グローバル状態変数は,#pragma assert func_attrs file_scope_vars 指示文によって関数に指定されるアサーションから除外されます。

この形式の指示文の構文は次のとおりです。

#pragma  assert global_status_variable(variable-list)

variable-list は変数のリストです。

この形式の #pragma assert 指示文は,ファイル範囲に記述しなければなりません。

variable-list 内の変数は,#pragma assert 指示文の位置から見えるところで宣言されていなければなりません。

3.1.3    #pragma assert non_zero

この形式の #pragma assert 指示文の構文は次のとおりです。

#pragma  assert non_zero(constant-expression) "string-literal"

コンパイラは,この指示文を見つけると constant-expression を評価します。 式がゼロであれば,コンパイラは,指定した文字列リテラルとコンパイル時定数の両方を含むメッセージを出力します。 たとえば,次のように指定したとします。

#pragma assert non_zero(sizeof(a) == 12) "a is the wrong size"
 

コンパイラは,sizeof(a) が 12 でないと判断した場合に次のようなメッセージを出力します。

cc: Warning: a.c, line 4: The assertion "sizeof(a)==12" was
    not true, a is the wrong size. (assertfail)

assert オプションの func_attrs および global_status_variable の場合と異なり,#pragma assert non_zero は関数本体の内部と外部のどちらにも置くことができます。 関数本体の内部で使用する場合,#pragma は文を記述できる位置に記述できますが,文として扱われることはありません。 関数本体の外部で使用する場合,#pragma は宣言を記述できる位置に記述できますが,宣言として扱われることはありません。

constant-expression 内の変数は,#pragma assert 指示文の位置から見えるところで宣言されていなければなりません。

#pragma assert はプラグマに対してマクロ置換を行わないので,#pragma assert_m 指示文を使用しなければならない場合があります。 次に示すような,struct のサイズと,そのいずれかの要素のオフセットの両方を確認するプログラムがあるとします。

#include <stddef.h>
typedef struct {
   int a;
   int b;
} s;
#pragma assert non_zero(sizeof(s) == 8) "sizeof assert failed"
#pragma assert_m non_zero(offsetof(s,b) == 4) "offsetof assert failed"

offsetof はマクロなので,2 番目の指示文を assert_m にして,offsetof が正しく展開されるようにしなければなりません。

3.2    #pragma environment 指示文

#pragma  environment 指示文を使用すると,すべてのコンテキスト・プラグマの状態を設定,保存,リストアすることができます。 コンテキスト・プラグマには,次のものがあります。

#pragma  extern_model
#pragma  extern_prefix
#pragma  member_alignment
#pragma  message
#pragma  pack
#pragma  pointer_size

コンテキスト・プラグマは以前の状態の保存とリストアを行うプラグマであり,通常は,同じタイプのプラグマを使用するヘッダ・ファイルを取り込む前後の状態です。

#pragma environment 指示文は,インクルード・ファイルを周囲のプログラムで設定されるコンパイル・コンテキストから保護するとともに,周囲のプログラムを,それがインクルードするヘッダ・ファイルで設定されるコンテキストから保護します。

このプラグマの構文は次のとおりです。

#pragma  environment [ command_line  |  header_defaults  |  restore  |  save ]
        

command_line

すべてのコンテキスト・プラグマの状態を,コマンド行に指定されたとおりに設定します。 このプラグマを使用すると,ヘッダ・ファイルが取り込まれる前に有効になる環境プラグマからヘッダ・ファイルを保護します。

header_defaults

すべてのコンテキスト・プラグマの状態を省略時の値に設定します。 これは,コマンド行オプションもプラグマも指定しないでプログラムをコンパイルした状況と同等ですが,このプラグマはヘッダ・ファイルに適するようにプラグマ・メッセージ状態を #pragma nostandard に設定します。

restore

すべてのコンテキスト・プラグマの現在の状態をリストアします。

save

すべてのコンテキスト・プラグマの現在の状態を保存します。

ソース・コードを変更しなくても,#pragma environment を使用することにより,追加のコンパイル・コンテキストをもたらす可能性のある言語の拡張などからヘッダ・ファイルを保護することができます。

ヘッダ・ファイルは取り込んだファイルからプラグマの状態を選択して継承することができ,その後,追加のプラグマを必要に応じて使用することにより,コンパイルを非省略時の状態に設定します。 次の例を参照してください。

#ifdef _ _pragma_environment
#pragma _ _environment save  [1]
#pragma _ _environment header_defaults [2]
#pragma member_alignment restore [3]
#pragma member_alignment save [4]
#endif
.
.    /*contents of header file*/
.
#ifdef _ _pragma_environment
#pragma _ _environment restore
#endif

この例に関する説明は次のとおりです。

  1. すべてのコンテキスト・プラグマの状態を保存します。 [例に戻る]

  2. 省略時のコンパイル環境を設定します。 [例に戻る]

  3. #pragma _ _environment save によってプッシュされた #pragma member_alignment スタックから,メンバ位置合わせコンテキストをポップして,メンバ位置合わせコンテキストを以前の状態にリストアします。 [例に戻る]

  4. メンバ位置合わせコンテキストをスタックにプッシュして戻し,#pragma _ _environment  restore がエントリをポップできるようにします。 [例に戻る]

このように,ヘッダ・ファイルが継承するメンバ位置合わせコンテキストを除き,ヘッダ・ファイルはすべてのプラグマから保護されています。

3.3    #pragma extern_model 指示文

#pragma extern_model 指示文は,外部リンケージを持つデータ・オブジェクトをコンパイラがどう解釈するかを制御します。 このプラグマを使用すると,外部 (extern) オブジェクトに使用するグローバル・シンボル・モデルを次のモデルの中から選択できます。

relaxed_refdef

このモデルでは,宣言には参照と定義があります。 同じオブジェクトに対して初期化されていない定義が複数あっても構いません。 リンカが解決して 1 つにまとめます。 ただし,参照するためには少なくとも 1 つの定義がなければなりません。 このモデルはほとんどの UNIX システムの C コンパイラで使用され,Compaq C の省略時モデルもこのモデルになっています。

strict_refdef

このモデルでは,宣言には参照と定義があります。 参照されるどのシンボルに対しても,プログラム内での定義は正確に 1 つだけでなければなりません。 このモデルは,ANSI C への厳密な準拠が保証される唯一のモデルです。

注意

HP OpenVMS プラットフォームの Compaq C では,これ以外に,common_block および globalvalue という名前の 2 つのモデルをサポートしていますが,これらは Tru64 UNIX ではサポートしていません。

extern_model プラグマでグローバル・シンボル・モデルを選択すると,別の extern_model プラグマを指定するまで,外部記憶クラスを持つオブジェクトの宣言は,すべて指定したモデルに従って処理されます。

たとえば,次のようなプラグマがあるとします。

#pragma extern_model strict_refdef "progsec3"

このプラグマを指定すると,それ以降のファイルレベルの宣言は,strict_refdef モデルに従ったグローバル・シンボルの宣言として処理されます。

int x = 0; 
extern int y;

外部モデルが何であっても,コンパイラは ANSI C の規則に従って宣言が定義と参照のどちらかを判断します。 外部定義は,記憶クラス・キーワードがないファイルレベルの宣言か,または記憶クラス・キーワード extern を含むファイルレベルの宣言で,初期化されているものです。 外部参照は,記憶クラス・キーワード extern のある宣言で,初期化されていないものです。 上記の例では,x の宣言はグローバル定義であり,y の宣言はグローバル参照です。

ヘッダ・ファイルとプログラム・テキストの小さい範囲で,他の部分への影響なしに #pragma extern_model を使用できるように,コンパイラの外部モデル状態のスタックが用意されています。 詳細は,3.3.4 項 および 3.3.5 項 を参照してください。

以下の項では,#pragma extern_model 指示文のさまざまな形式について説明します。

3.3.1    構文

#pragma extern_model 指示文の構文は次のとおりです。

#pragma  extern_model model_spec [attr[,attr]...]

model_spec

次のいずれかです。

relaxed_refdef
strict_refdef "name"

"name" は,任意の定義に対するプログラム・セクション (psect) の名前です。

[attr[,attr]...]

psect 属性の指定 (省略可能)。 以下に示す属性指定セットの中から 1 つだけ選択します。

shr|noshr

psect は,メモリ内で共有できる (shr) 場合と共有できない (noshr) 場合があります。 省略時は noshr です。

wrt|nowrt

psect に含まれるデータは,変更できる (wrt) 場合と変更できない (nowrt) 場合があります。 省略したときは,psect 内の最初の変数によって決まります。 変数に const 型修飾子 (または readonly 識別子) がある場合,psect は nowrt に設定されます。 それ以外の場合は,wrt に設定されます。

ovr|con

同じ名前の psect は,連結されるか (con),または同じメモリ位置にオーバレイされます (ovr)。 省略時は,strict_refdef では conrelaxed_refdef では over になります。

4|octa|5|6|7|8|9|10|11|12|13|14|15|16|page

これは,数値データの整列方法を指定します。 省略時の整列は octa です。 数値を指定すると,psect は,2 のその数値乗の位置に整列されます。

strict_refdef extern_model では,次のような psect 属性を指定することもできます。

noreorder

この属性を指定すると,セクション内の変数が,定義した順に割り付けられます。 この属性指定は,省略時はオフになっています。

次の例では,初期化された変数が 64 KB (2**16) の境界に整列されます。 noreorder 属性指定は,変数が宣言された順に割り付けられることを意味します。

#pragma extern_model save 
#pragma extern_model strict_refdef "progsecA" 16,noreorder
int var1 = 5;
int var2 = 6;
#pragma extern_model restore 
 
 

次の例では,(書き込み不可の) 変数が,データ・キャッシュ・ラインの境界に整列されます。

#pragma extern_model save 
#pragma extern_model strict_refdef "progsecB" 3,noreorder,nowrt
const long c_v1 = 1;
const long c_v2 = 2;
const long c_v3 = 2;
const long c_v4 = 2;
#pragma extern_model restore

relaxed_refdef モデルでは,psect 属性は,仮定義で宣言した変数に影響を与えません。 次のようなコードがあったとします。

#pragma extern_model relaxed_refdef 5
int a;
int b=6;
#pragma extern_model strict_refdef 5
int c;
 
 

変数 a は仮定義なので,省略時の octa ワード (2**4 つまり 16 バイト) の境界に整列されます。 しかし,b は初期化されているので 32 バイト (2**5) の境界に整列されます。 c は仮定義ですが,strict_refdef モデルで定義されているので 32 バイト (2**5) の境界に整列されます。

注意

通常,psect 属性はシステム・プログラマが使用します。 システム・プログラマは,通常はマクロ内で行われる宣言をしなければなりません。 これらの属性のほとんどは,通常の C プログラムでは不要です。

3.3.2    #pragma extern_model relaxed_refdef

このプラグマは,コンパイラの外部データのモデルを,UNIX システムで使用される relaxed_refdef モデルとします。

#pragma extern_model relaxed_refdef 指示文の構文は,次のとおりです。

#pragma  extern_model relaxed_refdef [attr[,attr]...]

3.3.3    #pragma extern_model strict_refdef

このプラグマは,コンパイラの外部データのモデルを strict_refdef モデルとします。 このモデルは,プログラムを ANSI C に厳密に準拠させたいときに使用します。

#pragma extern_model strict_refdef 指示文の構文は,次のとおりです。

#pragma  extern_model strict_refdef "name" [attr[,attr]...]

引用符に囲まれた name を指定すると,定義に対する psect の名前になります。

3.3.4    #pragma extern_model save

このプラグマは,コンパイラの現在の外部モデルをスタックにプッシュします。 スタックには,shr/noshr 状態や引用符で囲まれた psect 名など,外部モデルに対応する情報がすべて記録されます。

このプラグマの構文は次のとおりです。

#pragma  extern_model save

#pragma extern_model スタックに保存できるエントリの数は,コンパイラが使用できるメモリ容量のみによって制限されます。

3.3.5    #pragma extern_model restore

このプラグマは,コンパイラの外部モデルのスタックをポップします。 スタックからポップされた内容で外部モデルの状態が設定されます。 スタックには,shr/noshr 状態や引用符で囲まれた psect 名など,外部モデルに対応する情報がすべて記録されます。

このプラグマの構文は次のとおりです。

#pragma  extern_model restore

空のスタックをポップすると,警告メッセージが出力され,コンパイラの外部モデルは変わりません。

3.4    #pragma extern_prefix 指示文

#pragma  extern_prefix 指示文は,コンパイラの外部名合成を制御します。 リンカはこの外部名を使って,外部名要求を解決します。

文字列引数を使って #pragma  extern_prefix を指定すると,C コンパイラは,プラグマ指定に続く宣言によって生成されたすべての外部名の冒頭にその文字列を付加します。

このプラグマは,ライブラリを作成する上で有用です。 このライブラリを利用して,機能コードをライブラリ内の外部名に付加できます。

このプラグマの構文は,次のとおりです。

#pragma  extern_prefix "string" [(id,...)]

#pragma  extern_prefix save

#pragma  extern_prefix restore

引用符で囲まれた string は,プラグマ指定に続く宣言内の外部名に付加されます。

オプション・リスト [(id,...)] を使って,特定の外部識別子に接頭語を指定することもできます。

save および restore キーワードは,それぞれ現在のプラグマ接頭語文字列の保存,および保存済みのプラグマ接頭語文字列のリストアに使用できます。

プラグマによって指定されなかった場合,省略時の外部識別子用の接頭語は空文字列です。

推奨される使用法は,次のとおりです。

#pragma extern_prefix save
#pragma extern_prefix " prefix-to-prepend-to-external-names "
...some declarations and definitions ...
#pragma extern_prefix restore

extern_prefix が有効であり,#include を使ってヘッダ・ファイルをインクルードしているときに,extern_prefix をヘッダ・ファイルの extern 宣言に適用したくない場合は,次の順序でコードを記述します。

#pragma extern_prefix save
#pragma extern_prefix ""
#include ...
#pragma extern_prefix restore

上記の順序で指定しない場合,インクルードされたファイル内での定義について,接頭語が外部識別子の冒頭に付加されます。

注意

#pragma extern_prefix でオプションの識別子を指定する際に,以下の内容が適用されます。

3.5    #pragma inline 指示文

関数のインライン化とは,関数呼び出しのインライン展開を意味します。 つまり,関数呼び出しを関数コードそのものと置換します。 関数のインライン展開は,関数呼び出しのオーバヘッドを無くすとともに,展開したコードにコンパイラの一般的な最適化手法を適用することによって,実行時間を短縮します。 マクロの使用と比較すると関数のインライン化には次のような利点があります。

次のプリプロセッサ指示文が,関数のインライン化を制御します。

#pragma  inline (id, ...)

#pragma  noinline (id, ...)

このとき,id は関数 ID です。

関数をインライン展開する場合は,その関数定義を関数呼び出しと同じモジュールに記述しておく必要があります (-ifo オプションを指定してモジュール間でのインライン展開を可能にしている場合を除く)。 この定義の位置は,関数呼び出しの前でも後でも構いません。

cc コマンドにオプション -O3-O4-inline  size-inline  speed,あるいは -inline  all が指定されている場合,コンパイラは,#pragma  inline あるいは #pragma  noinline 指示文のどちらにも指定されていない関数の呼び出しのうち適切なものに関して展開します。 この場合,展開するかどうかは次の関数特性によって決定されます。

最適化レベル -O2 では,C コンパイラは小さな静的ルーチンだけをインライン化します。

#pragma inline 指示文は,大きさや呼び出し回数にかかわらずインライン展開するようにします。

3.6    #pragma intrinsic および #pragma function 指示文

intrinsic として宣言できる関数もあります。 Intrinsics 関数の中では,ある状況で関数呼び出しを避けるために,C コンパイラが最適化コードを生成します。

表 3-1 に Intrinsics として宣言できる関数を示します。

表 3-1:  Intrinsics 関数

abs fabs labs
printf fprintf sprintf
strcpy strlen memcpy
memmove memset alloca
bcopy bzero  

関数が Intrinsics として扱われるかどうかを制御するには,次の指示文のうちのいずれかを使用します。 func_name_list は,コンマで区切った複数の関数名をカッコで囲んだリストです。

#pragma  intrinsic (func_name_list)

#pragma  function (func_name_list)

#pragma  function  ()

#pragma  intrinsic 指示文は,関数の Intrinsics としての処理を使用可能にします。 #pragma  intrinsic 指示文が有効に設定されると,コンパイラは関数がどのように動作するかを認識するため,より効率的なコードを生成します。 関数の宣言は,プラグマが処理されるときに有効でなければなりません。

#pragma  function 指示文は,関数の Intrinsics としての処理を使用不能にします。 空の func_name_list を持つ関数プラグマは,すべての関数に対する Intrinsics としての処理を使用不能にします。

コンパイラに対応する組み込み (built-in) を持つ標準ライブラリ関数もあります。 組み込みは関数の同義名で,関数を Intrinsics として宣言する処理に似ています。 次の表のような組み込みが提供されています。

関数 同義名
abs _ _builtin_abs
labs _ _builtin_labs
fabs _ _builtin_fabs
alloca _ _builtin_alloca
strcpy _ _builtin_strcpy

Intrinsics や組み込み関数は,いくつかの手法で使用することができます。 関数の宣言を持つヘッダ・ファイルは,表 3-1 に示す関数用の #pragma  intrinsic 指示文を持っています。 このプラグマを使用可能にするには,プリプロセッサ・マクロ _INTRINSICS を定義しなければなりません。 alloca に対しては,alloca.h をインクルードするだけです。

たとえば,abs の Intrinsics 関数を得るためには,プログラムは stdlib.h をインクルードして,-D_INTRINSICS でコンパイルするか,または stdlib.h をインクルードする前に,_INTRINSICS#define 指示文で定義する必要があります。

組み込み処理を使用可能にする方法の 1 つに,-D スイッチを使用する方法があります。 たとえば,fabs の組み込みを使用可能にするには,次のいずれかの方法でコンパイルします。

% cc -Dfabs=_ _builtin_fabs prog.c
% cc -Dabs=_ _builtin_abs prog.c

ここまでの関数の Intrinsics としての処理は,関数とその使用方法によって異なります。 最適化の結果は次のとおりです。

3.7    #pragma linkage 指示文

#pragma  linkage 指示文を使用すると,リンケージ・タイプを指定することができます。 リンケージ・タイプは,関数によるレジスタの使用方法を指定します。 これを使用すると,関数が使用するレジスタを指定することができます。 また,関数の特性 (たとえば,パラメータを引き渡したり,値を返すレジスタ) や,関数が変更できるレジスタも指定することができるようになります。 #pragma  use_linkage 指示文は,以前に定義されたリンケージを関数と対応付けます (3.14 節を参照)。

#pragma linkage 指示文は,(関数が C で書かれている場合) 呼び出し側と関数コンパイルの両方に作用します。 関数がアセンブラで書かれている場合は,リンケージ・プラグマを使用して,アセンブラがレジスタを使用する方法を記述することができます。

#pragma linkage 指示文の構文は,次のとおりです。

#pragma  linkage linkage-name = (characteristics)

linkage-name

定義するリンケージ・タイプを識別します。 C 識別子の形式で指定します。 リンケージ・タイプは固有の名前空間を持っているため,コンパイル単位内の他の識別子やキーワードと競合することはありません。

characteristics

パラメータが引き渡される場所,関数の結果が返される場所,および関数呼び出しによって変更されるレジスタに関する情報を指定します。

register-list を指定する必要があります。 register-list は,rn または fn のいずれかのレジスタ名をコンマで区切ったリストです。 register-list にはカッコで囲んだサブリストを含めることができます。 register-list を使用して,構造体の引数や関数の結果型を記述します。 このとき,構造体の各メンバは単一のレジスタで引き渡されます。 たとえば,次のように指定します。

parameters(r0,(f0,f1))
 

これは 2 つのパラメータを持つ関数の例です。 最初のパラメータはレジスタ r0 に引き渡されます。 2 番目のパラメータは 2 つの浮動小数点メンバを持つ構造体型であり,レジスタ f0f1 に渡されます。

次の characteristics のリストは,項目をコンマで区切ってカッコで囲んで指定することができます。 これらのキーワードは任意の順に指定できます。

  • parameters (register-list)

    parameters 特性は,引数を特定のレジスタでルーチンへ引き渡します。

    register-list の各項目には,ルーチンに引き渡す 1 つのパラメータを記述します。

    構造体の引数は値によって引き渡すことができますが,構造体の各メンバは別々のパラメータ位置に引き渡されるという制約があります。 ただし,このようにすると,多数のレジスタが使用されるため,作成されるコードの処理速度が遅くなります。 コンパイラは,この状態を診断しません。

    parameters オプションで有効なレジスタは,r0 から r25 までの整数レジスタと,f0 から f30 までの浮動小数点レジスタです。

    構造体型は,各フィールドに対し最低 1 つのレジスタを必要とします。 構造体型で必要なレジスタ数が,プラグマで提供される数と同じであることがコンパイラによって確認されます。

  • result  (register-list)

    コンパイラは,関数が値を返すためにどのレジスタを使用するかを認識しておく必要があります。 この情報は result 特性を使用して引き渡します。

    関数が値を返さない (つまり,関数のリターン型が void) 場合は,リンケージの一部として result を指定しないでください。

    register オプションで有効なレジスタは,r0 から r25 までの汎用レジスタと,f0 から f30 までの浮動小数点レジスタです。

  • preserved (register-list)
    nopreserve (register-list)
    notused (register-list)
    notneeded ((lp))

    コンパイラは,関数によって使用されるレジスタと使用されないレジスタを区別するとともに,使用されるレジスタは関数呼び出しの間に保存されているかどうかを認識しておく必要があります。 この情報を指定するには,preservednopreservenotused,および notneeded のオプションを使用します。

    • preserved レジスタは,関数呼び出しの前後で同じ値を保持します。

    • nopreserve レジスタは,関数呼び出しの前後で同じ値を保持しているとはかぎりません。

    • notused レジスタは,呼び出された関数で使用されることはありません。

    • notneeded 特性は,特定の項目がこのリンケージを使用するルーチンで必要ないことを示します。 lp キーワードは,指定された関数を呼び出すときに,リンケージ・ポインタ・レジスタ (r27) を設定する必要がないことを指定します。 リンケージ・ポインタが必要になるのは,呼び出された関数がグローバルまたは static データにアクセスする場合です。 レジスタが必要ないことを指定するのが有効かどうかを判断する必要があります。

    preservednopreservenotused のオプションで有効なレジスタは,r0 から r30 までの汎用レジスタと,f0 から f30 までの浮動小数点レジスタです。

#pragma  linkage 指示文では,ネストした副構造体を含む構造体は,パラメータまたは特殊リンケージを持つ関数のリターン型としてはサポートされません。 関連する特殊リンケージを持つ関数は,共用型を持つパラメータまたはリターン型はサポートしません。

次の特性は,レジスタ f3f4 の 2 つの要素を含む simple-register-list と,レジスタ r0 とサブリスト (レジスタ f0f1 を含む) の 2 つの要素を含む register-list を指定します。

nopreserve(f3,f4)
parameters(r0,(f0,f1))
 

次の例は,そのような特性を使用するリンケージを示しています。

#pragma linkage my_link=(nopreserve(f3,f4),
                parameters(r0,(f0,f1)),
                notneeded (lp))
 

register-list のカッコで囲んだ表記法は,引数と struct 型の関数リターン値を記述します。 この struct の各メンバは,単一のレジスタに引き渡されます。 次の例では,sample_linkage は 2 つのパラメータを指定します。 最初のパラメータはレジスタ r0r1,および r2 に引き渡され,2 番目のパラメータは f1 に引き渡されます。

struct sample_struct_t {
    int A, B;
    short C;
    } sample_struct;
 
#pragma linkage sample_linkage = (parameters ((r0, r1, r2), f1))
void sub (struct sample_struct_t p1, double p2) { }
 
main()
{
    double d;
 
    sub (sample_struct, d);
}
 

3.8    #pragma member_alignment 指示文

省略時の設定では,コンパイラは構造体のメンバを自然境界に合わせます。 構造体メンバのバイト合わせを指定する場合は,#pragma  [no]member_alignment プリプロセッサ指示文を使用してください。

このプラグマの構文は次のとおりです。

#pragma  member_alignment [save  |  restore]

#pragma  nomember_alignment [base_alignment]

save  |  restore

パック境界合わせを含め,メンバの境界合わせの現在の状態を保管するために,あるいは前の状態をリストアするためにそれぞれ使用できます。 状態の制御は,member_alignment あるいは nomember_alignment を必要とするヘッダ・ファイル,もしくは,すでに設定されている member_alignment を含む必要のあるヘッダ・ファイルの作成のために必要になります。

base_alignment

base_alignment パラメータを使用すると,構造体のベース境界合わせを指定できます。 base_alignment には,次のキーワードのいずれかを使用します。

  • byte (1 byte)

  • word (2 bytes)

  • longword (4 bytes)

  • quadword (8 bytes)

  • octaword (16 bytes)

構造体メンバの自然境界合わせをリストアするには,#pragma  member_alignment を使用します。 #pragma  member_alignment を使用すると,コンパイラは,構造体メンバを次のバイトではなくそのメンバのタイプに合った次の境界に合わせます。 たとえば,int 変数は次のロングワード境界に合わせられ,short 変数は次のワード境界に合わせられます。

#pragma  nomember_alignment を使用すると,構造体メンバをバイト境界合わせに指定します。

pragma  pack 指示文では,構造体メンバの境界合わせをバイト,ワード,ロングワード,あるいはクォドワードに指定することができます。 #pragma  pack についての詳細は,3.11 節を参照してください。

#pragma  member_alignment#pragma  nomember_alignment および #pragma  pack の各プラグマの設定は,次のプラグマを処理するまで有効です。

3.9    #pragma message 指示文

#pragma message 指示文は,個々の診断メッセージまたは診断メッセージ・グループの発行を制御します。 このプラグマは,メッセージの発行に関する他のどのコマンド行オプションよりも優先します。

#pragma  message 指示文の構文は,次のとおりです。

#pragma  message option1 (message-list)

#pragma  message option2

#pragma  message ("string")

3.9.1    #pragma message option1

この形式の #pragma message 指示文の構文は,次のとおりです。

#pragma  message option1 (message-list)

option1 パラメータは,以下のキーワードのいずれかでなければなりません。

enable

メッセージ・リストで指定したメッセージの発行を有効にします。

disable

メッセージ・リストで指定したメッセージの発行を無効にします。 無効にできるメッセージは,重大度が Warning または Information の場合に限られます。 メッセージの重大度が Error または Fatal の場合,そのメッセージは無効にしようとしても関係なく発行されます。

emit_once

指定したメッセージは,コンパイル時に 1 度だけ出力されます。 メッセージには,コンパイラがその原因となる条件を初めて検出した場合にのみ出力されるものがあります。 コンパイラがその後,プログラムの中で同じ条件を検出しても,メッセージは出力されません。 このようなメッセージには,たとえば言語拡張の使用に関するメッセージがあります。 原因となる条件を検出するたびに毎回このようなメッセージを出力するには,emit_always オプションを使用します。

Errors と Fatals のメッセージは必ず出力されます。 このようなメッセージに emit_once を指定することはできません。

emit_always

条件を検出するたび,毎回メッセージを出力します。

error

指定したメッセージの重大度を Error に変更します。 Error および Fatal メッセージは,重大度をそれより低くすることはできません。 (例外として,メッセージを Error から Fatal に上げ,次に Error に下げることはできますが,Error から下げることはできません。 Warning と Informational については,重大度を自由に変更できます。)

fatal

指定したメッセージの重大度を Fatal に変更します。

informational

指定したメッセージの重大度を Informational に変更します。 Fatal および Error メッセージは重大度を低くすることができないことに注意してください。

warning

message-list 内の各メッセージの重大度を Warning に変更します。 Fatal および Error メッセージは重大度を低くすることができないことに注意してください。

message-list パラメータは,以下のいずれかになります。

注意

省略時は,選択したコンパイラ・モードに対する診断メッセージがすべて発行されます。 ただし,check グループは例外であり,メッセージの表示を明示的に有効にしなければなりません。

3.9.2    #pragma message option2

この形式の #pragma message 指示文の構文は,次のとおりです。

#pragma  message option2

option2 パラメータは,次のキーワードのいずれかでなければなりません。

save

どのメッセージが有効 (無効) になっているかという現在の状態を保存します。

restore

どのメッセージが有効 (無効) になっているかという,直前の状態をリストアします。

save および restore オプションは,主にヘッダ・ファイル内で役に立ちます。

3.9.3    #pragma message ("string")

この形式の #pragma message 指示文は,Microsoft の #pragma message 指示文との互換性のためのもので,構文は次のとおりです。

#pragma  message ("string")

この指示文は,指定された string をコンパイラ・メッセージとして出力します。 たとえば,コンパイラがソース・ファイル内で次のような行に遭遇したとします。

#pragma message ("hello")

すると,コンパイラは,次のように出力します。

cc: Info: a.c, line 10: hello (simplemessage)
#pragma message ("hello")
----------------^
 
 

この形式のプラグマでは,マクロの置き換えが実行されます。 たとえば,次のような使い方ができます。

#pragma message ("Compiling file " _ _FILE_ _)
 

3.10    #pragma optimize 指示文

#pragma  optimize 指示文は,その指示文に続く関数定義の最適化の特性を設定します。 このプラグマを使用すると,通常はコマンド行でコンパイル全体に対して設定する最適化制御オプションを,ソース・ファイル内で個別の関数に対して指定できます。 このプラグマの形式は次のとおりです。

#pragma  optimize settings

#pragma  optimize save

#pragma  optimize restore

#pragma  optimize command_line

save および restore オプションは,現在の最適化の状態 (level, unroll count, ansi-alias setting, intrinsic setting) の保存と復元を行います。

command_line オプションを使用すると,最適化設定は,コンパイル時にコマンド行オプション cc で指定した時点で有効な設定に戻ります。

settings は,以下の値を組み合わせて指定します。

level

level は,最適化レベルを設定します。 レベルは次のように指定します。

level=n

ここで,re n は 0 〜 5 の整数です。

0

すべての最適化を無効にします。 代入されていない変数のチェックを行いません。

1

ローカルな最適化と,一部の共通部分式の認識を有効にします。 コール・グラフによって,プロシージャをコンパイルする順序が決まります。

2

level 1 の最適化を含みます。 グローバルな最適化を有効にします。 この最適化には,データフローの分析,コードの移動,強度の軽減,テストの置換,存在期間分割の分析,コードのスケジューリングが含まれます。

3

level 2 の最適化を含みます。 さらにグローバルな最適化を有効にして実行速度を改善します (コード・サイズは増える)。 たとえば,整数の乗算および除算の展開 (シフトを使用),ループの展開,コードの繰り返しによる分岐の削除を行います。

4

level 3 の最適化を含みます。 プロシージャの相互作用の分析と小プロシージャの自動インライン化 (追加コード量は発見的に制限される) を有効にします。 これが省略時のレベルです。

5

level 4 の最適化を含みます。 ソフトウェアのパイプライン化をアクティブにします。 これはループ展開の特別な形式であり,実行時の性能が改善できる場合があります。 ソフトウェアのパイプライン化では,命令スケジューリングを用いてループ内での命令の停止をなくし,展開されていない複数のループに命令を再配置して性能を改善します。

ソフトウェアのパイプライン化の候補になるループは常に,最も内側にあって分岐やプロシージャの呼び出しを含まないループです。 ユーザのプログラムで level 5 を使用して効果があるかどうかを判断するには,同じプログラムを level 4level 5 でコンパイルして,プログラムの実行にかかる時間を測定する必要があります。 使用可能なレジスタを使い尽くすようなループのあるプログラムでは,level 5 の方が実行時間が長くなります。

unroll

unroll 設定はループの展開を制御します。 次のように指定します。

unroll=n

ここで,n は負でない整数です。 unroll= n は,ループ本体を n 回展開することを意味します。 ここで,n016 の数値です。 unroll=0 は,最適化プログラムの省略時の展開回数を使用することを意味します。 unroll は,レベル 3 以上の最適化で使用します。

ansi-alias

ansi-alias は,ansi-alias の仮定条件を制御します。 次のいずれかを指定します。

ansi_alias=on
ansi_alias=off

intrinsic

intrinsic は,intrinsic の認識を制御します。 次のいずれかを指定します。

intrinsics=on
intrinsics=off

setting 句の間と,各句の "=" の前後のホワイト・スペースはオプションです。 pragma ではマクロ置換は行われません。

例:

#pragma optimize level=5 unroll=6

使用上の注意

3.11    #pragma pack 指示文

#pragma  pack 指示文は,構造体の全メンバに関する境界合わせの制約を変更します。 指示文は,構造体全体に対して作用するため,構造体の定義全体の前に指定しなければなりません。 このプラグマの構文は,次のとおりです。

#pragma  pack {n | (n) | (  )}

n は,その後に続く構造体メンバが n バイト境界に位置合わせされることを指定する数字 (1,2,4 など) です。 n に 0 (ゼロ) を指定した場合には,境界合わせは省略時の設定に戻ります。 これは,cc コマンドの -Zpn オプションで設定されている場合があります。

pragma  pack 指示文は,Microsoft Visual C++ でサポートしている push および pop 引数もサポートしています。

#pragma  pack ( {push | pop} [, identifier] [, n])

push および pop 引数を使用すると,プログラムのコンポーネントまたがって使用する境界合わせの値を保存したり復元したりすることができます。 これにより,境界合わせの指定が異なる場合でも,コンポーネントを単一の変換ユニットに連結できます。

pragma  packpush および pop 引数を使用すると,ヘッダ・ファイルを検出した前と後で,境界合わせの値が確実に同じになるように,ヘッダ・ファイルを記述することができるようになります。 以下に例を示します。

/* File name: myinclude.h */
#pragma pack (push,enter_myinclude)
.
.  (your include file code)
.
#pragma pack (pop,enter_myinclude)
/* End of myinclude.h */

この例では,現在の境界合わせの値が識別子の enter_myinclude に関連付けられ,ヘッダ・ファイルの処理の前にプッシュされます。 次に,インクルード・ファイルのコードが処理されます。 インクルード・ファイルの処理が完了すると,ヘッダ・ファイルの末尾にある #pragma  pack が,ヘッダ・ファイル内で発生する可能性のある中間の境界合わせ値と,enter_myinclude に対応する境界合わせの値を削除して,ヘッダ・ファイルの前と後で境界合わせの値が同じになるようにします。

push および pop 引数を使用して,現プログラムコードの設定と異なる境界合わせの値を設定するようなヘッダ・ファイルをインクルードすることもできます。 以下に例を示します。

#pragma pack (push,before_myinclude)
#include <myinclude.h>
#pragma pack (pop,before_myinclude)

この例では,現在の境界合わせ値の保存,インクルード・ファイルの処理 (境界合わせ値がどうなるかは不明),オリジナルの境界合わせ値の復元と処理することにより,myinclude.h で発生する可能性のある境界合わせ値の変更から,コードを保護しています。

3.12    #pragma pointer_size 指示文

#pragma  pointer_size 指示文は,次の項目に関するポインタ・サイズの割り当てを制御します。

このプラグマの構文は次のとおりです。

#pragma  pointer_size { long  |  short  |  64  |  32 } | { save  |  restore }
        

long  |  64

コンパイラが別の #pragma  pointer_size 指示文を処理するまでの間,この指示文に続いて宣言されているすべてのポインタ・サイズを 64 ビットに設定します。

short  |  32

コンパイラが別の #pragma  pointer_size 指示文を処理するまでの間,この指示文に続いて宣言されているすべてのポインタ・サイズを 32 ビットに設定します。

save  |  restore

それぞれ,現在のポインタ・サイズの保管,および保管されているポインタ・サイズのリストアを行います。 save および restore オプションは,混合ポインタのサポートおよび旧オブジェクトへのインタフェースとなるヘッダ・ファイルの保護のため,特に有用です。 複数のポインタ・サイズ・プラグマでコンパイルされたオブジェクトは,古いオブジェクトと互換性がなく,コンパイラは互換性のないオブジェクトが混在していることを認識できません。

たとえば,次のとおりです。

 #pragma pointer_size long
   /* pointer sizes in here are 64-bits */
 #pragma pointer_size save
 #pragma pointer_size short
   /* pointer sizes in here are 32-bits */
 #pragma pointer_size restore
   /* pointer sizes in here are again 64-bits */

short ポインタの使用は,Tru64 UNIX システム上の Compaq C++ および Compaq C コンパイラに制限されています。 ルーチンが C プログラミング言語以外の言語で書かれている場合は,プログラムで C++ ルーチンから別のルーチンへ short ポインタを引き渡してはなりません。 また,Compaq C++ では,short ポインタを使用するアプリケーションで,short ポインタから long ポインタへ明示的に変換する必要があります。 まず,short ポインタの使用を検討しているアプリケーションを移植した後,それを分析して,short ポインタを使用することが有益であるかどうかを判断します。 関数定義におけるポインタ・サイズの相違は,関数にとってはそれほどのオーバロードではありません。

C コンパイラは,次の状態のいずれかを検出すると,エラー・レベルの診断メッセージを表示します。

3.13    #pragma unroll 指示文

#pragma  unroll 指示文は,その後の for ループで実行されるループ展開の量を制御します。 この指示文のフォーマットは次のとおりです。

#pragma  unroll (unroll-factor)

この指示文は,コンパイラに対して,unroll-factor 引数で指定した回数だけ for ループを展開するように指示します。

unroll-factor

0 から 255 までの整数の定数。 コンパイラは,指示文に続く for ループを,この回数だけ展開します。 値が 0 の場合,指示文は無視され,コンパイラは通常の方法でループの展開回数を判断します。 値が 1 の場合,ループは展開されません。

制御を行うには,指示文を for 文の直前に置く必要があります。 それ以外の位置に置くと,警告が発行され pragma は無視されます。

#pragma unroll (1) 
for (i=0; i<1000; i++) {foo(i);}

3.14    #pragma use_linkage 指示文

#pragma  linkage 指示文で特殊リンケージを定義した後は (3.7 節で説明),#pragma  use_linkage 指示文を使用して,リンケージを関数に関連付けます

このプラグマの構文は次のとおりです。

#pragma  use_linkage linkage-name (id1, id2, ...)
        

linkage-name

以前に #pragma linkage 指示文で定義したリンケージの名前を指定します。

id1id2...

指定したリンケージに関連付ける関数の名前または関数の型の typedef 名を指定します。

関数の型の typedef 名を指定すると,その型を用いて宣言した関数または関数へのポインタは,指定されたリンケージを持ちます。

#pragma  use_linkage 指示文は,ソース・ファイルで,指定したルーチンを使用したり定義する前に記述する必要があります。 このようにしない場合,予測できない結果が生じます。

次の例は,特殊リンケージを定義して,それをルーチンに対応付けます。 このルーチンは,3 つの整数パラメータをとり,最初のパラメータが引き渡された位置に 1 つの整数値を返します。

#pragma linkage example_linkage (parameters(r16, r17, r19), result(r16))
#pragma use_linkage example_linkage (sub)
int sub (int p1, int p2, short p3);
 
main()
{
    int result;
 
    result = sub (1, 2, 3);
}
 

この例では,result(r16) オプションは,関数の結果は通常の位置 (r0) ではなく,r16 に返されることを示しています。 parameters オプションは,sub に引き渡される 3 つのパラメータが r16r17,および r19 に渡されることを示しています。

次の例では,関数 f1 と関数の型 t はどちらもリンケージ foo を持ちます。 関数ポインタ f2 を用いて呼び出すと,特殊リンケージを用いて正しく関数 f1 が呼び出されます。

#pragma linkage foo = (parameters(r1), result(r4)) 
#pragma use_linkage foo(f1,t) 
 
int f1(int a); 
typedef int t(int a); 
 
t *f2; 
 
#include <stdio.h> 
 
main() { 
    f2 = f1; 
    b = (*f2)(1); 
} 
 

3.15    #pragma weak 指示文

#pragma  weak 指示文は,新たに弱外部シンボルを定義し,新しいシンボルと外部シンボルとを関連づけます。 このプラグマの構文は,次のとおりです。

#pragma  weak (secondary-name, primary-name)
        

強シンボルと弱シンボルについては,2.8 節を参照してください。