この章では、C コンパイラに固有の部分について説明します。言語の拡張と環境に分けて説明します。
C コンパイラは、新しい ISO C 規格である ISO/IEC 9899-1999 で規定されている C 言語のいくつかの機能と互換性があります。従来の C 規格である ISO/IEC 9889-1990 規格 (および修正 1) と互換性のあるコードをコンパイルすることを希望する場合は、-xc99=none を使用します。その結果、コンパイラは ISO/IEC 9899-1999 規格による拡張を無視します。
ここでは、Sun C コンパイラの定数に関する情報を掲載します。
次の表に示すように、10 進数、8 進数、16 進数の定数に接尾辞を付けて型を示すことができます。
表 2–1 データ型の接尾辞
-xc99=all を指定する場合、定数の大きさに応じて、次のリストから値が表現でき る最初の型を使用します。
int
long int
long long int
long long int で表現できる値の最大値を超えると、コンパイラは警告を発行します。
-xc99=none を指定すると、コンパイラが接尾辞を持たない定数の型を割り当てる場合、定数の大きさに応じて、次の中から値が表現できる最初の型を使用します。
エスケープシーケンスの発生しない複数バイト文字セットの値は、各文字の示す数値から派生しています。たとえば定数 ’123’ の持つ値は次のようになります。
0 |
’3’ |
’2’ |
’1’ |
あるいは 0x333231 です。
-Xs オプション使用の場合、あるいは ISO でないほかの C では、この値は次のようになります。
0 |
’1’ |
’2’ |
’3’ |
次の宣言指示子を使用して、extern シンボルの宣言と定義を隠せます。これらの指示子を使うと、リンカースコープのマップファイルは使用しなくてすみます。また、コマンド行で -xldscope を指定して、変数スコープのデフォルト設定を制御することもできます。詳細については、「B.2.102 -xldscope={v}」 を参照してください。
表 2–2 宣言指示子
オブジェクトまたは関数は、より制限された指示子で再宣言することはできますが、制限のよりゆるやかな指示子で再宣言することはできません。シンボルは一度定義したら、異なる指示子で宣言することはできません。
__global はもっとも制限の少ないスコープです。__symbolic はより制限されたスコープです。__hidden はもっとも制限の多いスコープです。
スレッドローカルの変数を宣言して、スレッドローカルな記憶領域を利用します。スレッドローカルな変数の宣言は、通常の変数宣言に変数指示子 __thread を加えたものから成ります。詳細については、「B.2.153 -xthreadvar[= o]」 を参照してください。
__thread 指示子は、コンパイル対象のソースファイルにあるスレッド変数の最初の宣言に含める必要があります。
__thread 指示子を使用できるのは、静的記憶領域を持つオブジェクトの宣言内だけです。スレッド変数を静的に初期化する方法は、静的記憶領域のほかのオブジェクトの場合と同じです。
__thread 指示子で宣言する変数は、__thread 指示子なしで宣言する場合と同じリンカー結合を持っています。初期設定子のない宣言など、一時的な定義が含まれます。
スレッド変数のアドレスは定数ではありません。したがって、スレッド変数のアドレス演算子 (&) は実行時に評価され、現在のスレッドのスレッド変数のアドレスが返されます。結果的に、静的記憶領域のオブジェクトはスレッド変数のアドレスに動的に初期化されます。
スレッド変数のアドレスは、対応するスレッドの有効期間の間は安定しています。変数の有効期間内は、プロセス内の任意のスレッドがスレッド変数のアドレスを自由に使用できます。スレッドが終了したあとは、スレッド変数のアドレスを使用できません。スレッドの終了後は、そのスレッドの変数のアドレスはすべて無効となります。
IEEE 754 のデフォルトの浮動小数点演算機能は「無停止」であり、アンダーフローは「段階的」です。ここでは概要を説明します。詳細については『数値計算ガイド』を参照してください。
「無停止」とは、ゼロによる除算、浮動小数点のオーバーフロー、不正演算例外などが生じても実行を停止しないことを意味します。たとえば次の式で、x はゼロ、y は正の数であるとします。
z = y / x;
デフォルトでは、z の値は +Inf になりますが、プログラムの実行は続けられます。ただし、-fnonstd オプションを使用した場合は、このコードによってプログラムが終了します (コアダンプなど)。
次に、段階的アンダーフローの動作を説明するために、次のようなコードを例として考えます。
x = 10; for (i = 0; i < LARGE_NUMBER; i++) x = x / 10; |
ループを 1 回通ると x は 1 になり、2 回目で 0.1、3 回目で 0.01 と続き、やがてはマシンによって値を表現できる許容範囲の下限に到達します。次にループを実行すると、どうなるのでしょうか。
表現可能な最小の数が 1.234567e-38 であると仮定します。
次にループを実行すると、仮数部から「盗んだ」ものを指数部に「与える」ことによって数値が修正され、新しい値 1.23456e-39 になります。その次はさらに、1.2345e-40 と続いていきます。これがデフォルト動作である「段階的アンダーフロー」です。非標準モードでは、仮数部から「盗む」ことをせず、通常は単に x をゼロに設定します。
C コンパイラは、計算型 goto 文として知られる C の拡張機能を認識します。計算型 goto 文を使用すると、実行時に分岐先を判別することができます。次のように演算子 '&&' を使用して、ラベルのアドレスを取得し、void * 型のポインタに割り当てることができます。
void *ptr; ... ptr = &&label1; |
あとに続く goto 文は、ptr により label1 に分岐できます。
goto *ptr; |
ptr は実行時に計算されるため、ptr は有効範囲内にある任意のラベルのアドレスを取得でき、goto 文はこのアドレスに分岐することができます。
ジャンプテーブルを実装するには、計算型 goto 文を次の方法で使用します。
static void *ptrarray[] = { &&label1, &&label2, &&label3 }; |
これで、次のようにインデックスを指定して配列要素を選択できます。
goto *ptrarray[i]; |
ラベルのアドレスは、現在の関数スコープからしか計算できません。現在の関数以外のラベルについてアドレスを取得しようとすると、予測できない結果になります。
ジャンプテーブルは switch 文と同様の働きをしますが、両者にはいくつかの相違点があり、ジャンプテーブルではプログラムフローの追跡がより困難になる可能性があります。顕著な相違点は、switch 文のジャンプ先は必ず予約語から順方向にあることです。計算型 goto 文を使用してジャンプテーブルを実装すると、順方向と反対方向の両方に分岐することができます。
#include <stdio.h> void foo() { void *ptr; ptr = &&label1; goto *ptr; printf("Failed!\n"); return; label1: printf("Passed!\n"); return; } int main(void) { void *ptr; ptr = &&label1; goto *ptr; printf("Failed!\n"); return 0; label1: foo(); return 0; } |
次の例では、プログラムフローの制御にジャンプテーブルを使用しています。
#include <stdio.h> int main(void) { int i = 0; static void * ptr[3]={&&label1, &&label2, &&label3}; goto *ptr[i]; label1: printf("label1\n"); return 0; label2: printf("label2\n"); return 0; label3: printf("label3\n"); return 0; } %example: a.out %example: label1 |
また、計算型 goto 文は、スレッド化されたコードのインタプリタとしても使用されます。高速ディスパッチを行うために、インタプリタ関数の範囲内にあるラベルアドレスを、スレッド化されたコードに格納することができます。
-xc99=none を指定してコンパイルを行う場合、Sun C コンパイラにはデータ型 long long および unsigned long long があり、これらはデータ型 long と類似しています。SPARC V8 や x86 では、long に 32 ビットの情報を格納できるのに対し、long long には 64 ビットの情報を格納できます。SPARC V9 では、long には 64 ビットの情報を格納できます。long long は -Xc モードでは使用できません。
long long データ型を出力または入力するには、変換指定子の前に ll の接頭辞を付けてください。たとえば、long long データ型を持つ変数 llvar を符号付き 10 進形式で出力するには、次のように指定します。
printf("%lld\n", llvar); |
2 項演算子によっては、両方のオペランドの型を共通の型にするために変換することがあります。この時、結果の型も共通の型となります。この変換は通常の算術変換と呼ばれます。
どちらか一方のオペランドが long double 型である場合、もう一方のオペランド は long double に変換されます。
一方のオペランドが double 型を持つ場合、もう一方のオペランドは double に変換されます。
一方のオペランドが float 型を持つ場合、もう一方のオペランドは float に変換されます。
これ以外の場合は、汎整数拡張が両方のオペランドで実行されます。次の規則が適用されます。
一方のオペランドが unsigned long long int 型を持つ場合、もう一方の演算子は unsigned long long int に変換されます。
一方のオペランドが long long int 型を持つ場合、もう一方の演算子は long long int に変換されます。
一方のオペランドが unsigned long int 型を持つ場合、もう一方の演算子は unsigned long int に変換されます。
SPARC V9 でコンパイルする場合のみ、cc -xc99=none を指定するときは、一方のオペランドが long int 型を持ち、もう一方が unsigned int 型を持つ場 合、両オペランドは unsigned long int に変換されます。
一方のオペランドが long int 型を持つ場合、もう一方のオペランドは long int に変換されます。
一方のオペランドが unsigned int 型を持つ場合、もう一方のオペランドは unsigned int に変換されます。
標準 C では、switch 文内にある case のラベルに、ただ 1 つの値を関連付けることができます。Sun C では、case 範囲として知られる、一部のコンパイラに見られる拡張を許可しています。
case 範囲は、値範囲を指定し、個別の case のラベルに関連付けます。case 範囲の構文は、次のとおりです。
case low ... high:
case 範囲は、low ~ high で指定された範囲内にある各値に対して case ラベルを指定した場合とまったく同じ動作をします (low と high が等しい場合は、case 範囲はただ 1 つの値を指定します)。下限と上限の値は、C 規格の要件に準拠している必要があります。つまり、これらの値は、有効な整数型の定数式であることが必要です (C 規格 6.8.4.2)。case 範囲と case ラベルは、自由に混在することができ、単一の switch 文内で複数の case 範囲を指定できます。
プログラミングの例:
enum kind { alpha, number, white, other }; enum kind char_class(char c); { enum kind result; switch(c) { case 'a' ... 'z': case 'A' ... 'Z': result = alpha; break; case '0' ... '9': result = number; break; case ' ': case '\n': case '\t': case '\r': case '\v': result = white; break; default: result = other; break; } return result; } |
case ラベルに関する既存の要件に対してエラー条件を追加:
- low の値が high の値より大きい場合、コンパイラはエラーメッセージを生成してコードを拒否します (他のコンパイラの動作は一貫していないので、他のコンパイラでコンパイルした場合にプログラムが異なる方法で動作するわけではないことを保証する唯一の方法は、エラー状況を発生させることです)。
- ある case ラベルの値が、switch 文の中ですでに使用された case 範囲内の範囲に含まれている場合、コンパイラはエラーメッセージを生成してコードを拒否します。
- case 範囲どうしの範囲が重複している場合、コンパイラはエラーメッセージを生成してコードを拒否します。
case 範囲の終点 (上限または下限) が数値リテラルである場合、省略記号 (...) の前後に半角スペースを記述し、ピリオドのいずれかが小数点として扱われることを防止してください。
次に例を示します。
case 0...4; // error case 5 ... 9; // ok |
#assert predicate (token-sequence) |
token-sequence は、表明の名前空間 (マクロ定義用の空間から分離されている) にある述語と関連付けられます。述語は識別子トークンでなければいけません。
#assert predicate |
これは述語が存在していることを表明しますが、それにトークン列を関連付けることはしません。
コンパイラは、次のような事前定義された述語をデフォルトとして提供しています (-Xc モードを除く)。
#assert system (unix) #assert machine (sparc) #assert machine (i386)(x86) #assert cpu (sparc) #assert cpu (i386)(x86) |
lint は、次のような事前定義された述語をデフォルトとして提供しています (-Xc モードを除く)。
#assert lint (on) |
表明は #unassert を使用して削除できます。この場合、assert と同じ構文が使用されます。引数なしで #unassert を使用すると述語に対するすべての表明が削除され、表明を指定すればその表明だけが削除されます。
表明は、次の構文を持つ #if 文でテストすることができます。
#if #predicate(non-empty token-list) |
たとえば次のように指定して、事前定義された述語 system をテストすることがで きます。
#if #system(unix) |
これは真と評価されます。
#pragma pp-tokens |
各処理系が定義した処理を指定します。
次の #pragmas はコンパイルシステムに認識されます。認識されなかったプラグマは無視されます。-v オプションを使用すると、認識されなかったプラグマについて警告が出されます。
#pragma align integer (variable[, variable] )
整列プラグマで指定した変数のメモリーはデフォルト値によらず、すべて integer バイト境界に揃えられます。ただし、次の制限があります。
integer は、1 ~ 128 の範囲にある 2 のべき乗、つまり、1、2、4、8、16、32、64、128 のいずれかでなければいけません。
variable には大域または静的な変数を指定します。自動変数は指定できません。
指定された境界がデフォルトより小さい場合は、デフォルトが優先します。
プラグマ行は、その行に指定される変数の宣言よりも先になければいけません。先にないと無視されてしまいます。
プラグマ行で記述されているが、そのあとで宣言されていない変数は無視されます。たとえば、次のようにします。
#pragma align 64 (aninteger, astring, astruct) int aninteger; static char astring[256]; struct astruct{int a; char *b;}; |
#pragma c99("implicit" | " no%implicit")
このプラグマは、暗黙的な関数宣言の診断を制御します。c99 プラグマの値が "implicit" に設定されている場合 (引用符を使用することに注意)、コンパイラが暗黙的な関数宣言を検出すると、警告が生成されます。c99 プラグマの値が "no%implicit" に設定されている場合 (引用符を使用することに注意)、プラグマの値がリセットされるまで、コンパイラは暗黙的な関数宣言をそのまま受け入れます。
-xc99 オプションの値は、このプラグマに影響を与えます。-xc99=all の場合はプラグマが #pragma c99("implicit") に設定され、-xc99=none の場合はプラグマが #pragma c99("no%implicit") に設定されます。
デフォルトでは、このプラグマは c99=("implicit") に設定されます。
#pragma does_not_read_global_data (funcname [, funcname])
リストに指定したルーチンが直接にも間接にも大域データを読み取らないことを表明します。この表明により、そうしたルーチンへの呼び出し前後のコードをさらに最適化することができます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。
指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。
#pragma does_not_return (funcname [, funcname])
指定した関数への呼び出しが復帰しないことをコンパイラに表明します。この表明により、コンパイラは、指定された関数への呼び出しが戻らないと仮定して最適化を行うことができます。たとえば、レジスタの存続期間が呼び出し元で終了する場合は、さらに最適化率を高めることができます。
指定した関数が復帰した場合は、プログラムの動作は未定義になります。次の例に示すように、このプラグマは、指定した関数をプロトタイプまたは空のパラメータリストで宣言したあとでのみ使用できます。
extern void exit(int); #pragma does_not_return(exit) extern void __assert(int); #pragma does_not_return(__assert) |
#pragma does_not_write_global_data (funcname [, funcname])
リストに指定したルーチンが直接にも間接にも大域データを書き込まないことを表明します。この表明により、そうしたルーチンへの呼び出し前後のコードをさらに最適化することができます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。
指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。
#pragma error_messages (on|off|default, tag… tag)
このエラーメッセージプラグマは、ソースプログラムの中から、C コンパイラおよび lint が発行するメッセージを制御可能にします。C コンパイラでは、警告メッセージに対してのみ有効です。C コンパイラの -w オプションを使用すると、このプラグマは無効になり、すべての警告メッセージが抑止されます。
#pragma error_messages (on, tag… tag)
on オプションは、先行する #pragma error_messages オプション (off オプションなど) をその時点で無効にして、-erroff オプションも無効にします。
#pragma error_messages (off, tag… tag)
off オプションは、C コンパイラまたは lint プログラムが指定トークンから始まる特定のメッセージを発行することを禁止します。この特定のエラーメッセージに対するプラグマの指定は、別の #pragma error_messages によって無効にされるか、コンパイルが終了するまで有効です。
#pragma error_messages (default, tag… tag)
default オプションは、指定タグについて、先行する #pragma error_messages 指令を無効にします。
#pragma fini (f1[, f2…,fn]
main() ルーチンを呼び出したあと、<f1> から <fn (終了関数) までの関数を呼び出します。この種の関数は、型が void で引数はあってはいけません。プログラム制御下でプログラムが終了したとき、またはこのプログラムを含む共有オブジェクトがメモリーから除去されたときに呼び出されます。次の「初期化関数」の場合と同様、終了関数もリンクエディタによって処理された順に実行されます。
大域プログラム状態が初期化関数の影響を受ける場合には注意が必要です。たとえばシステムライブラリ初期化関数を使用したときに何が発生するかがインタフェースに明示的に規定されていないかぎり、システムライブラリ初期化関数によって変更される可能性がある errno の値などの大域状態情報をすべて取得し、復元する必要があります。
このような関数は #pragma fini 指令の中に登場するたびに、1 回呼び出されます。
#pragma hdrstop
同じプリコンパイル済みヘッダーファイルを共有すべき各ソースファイルの活性文字列 (viable prefix) の最後を識別するために、hdrstop プラグマを最後のヘッダーファイルのあとに置く必要があります。たとえば次のファイルがあるとします。
example% cat a.c #include "a.h" #include "b.h" #include "c.h" #include <stdio.h> #include "d.h" . . . example% cat b.h #include "a.h" #include "b.h" #include "c.h" |
ソースファイルの活性文字列は c.h で終わっているので、各ファイルの c.h のあとに #pragma hdrstop を挿入します。
#pragma hdrstop は、cc コマンドで指定されるソースファイルの活性文字列の最後にのみ使用できます。#pragma hdrstop をインクルードファイル内に指定しないでください。
実行可能プログラムの .comment セクション内に任意の string を格納します。
#pragma init (f1[, f2…,fn] )
main() を呼び出す前に、f 1 から f n までの関数 (初期化関数) を呼び出します。この種の関数は、型が void で引数はあってはいけません。実行開始時にプログラムのメモリーイメージを構成しているときに呼び出されます。共有オブジェクトの中の初期値設定子は、その共有オブジェクトをメモリー内へ持っていく動作の間、つまりプログラムの開始中、または dlopen() のような動的ロード動作中に実行されます。初期化関数の呼び出しを順序付ける方法は、それがリンクエディタによって動的または静的に処理される順序に依存します。
大域プログラム状態が終了関数の影響を受ける場合には注意が必要です。たとえばシステムライブラリを終了関数として使用したときに発生する事柄は、インタフェースに明示的に規定されていません。そういった情報が無い状態で、終了関数として使われたシステムライブラリが変更した可能性がある errno の値などの大域状態情報をすべて捕捉して、復元する必要があります。
このような関数は #pragma init 指令の中に登場するたびに、1 回呼び出されます。
#pragma [no_]inline (funcname[, funcname])
指定したルーチン名のインライン化を制御します。このプラグマはファイル全体に対して有効です。このプラグマでは、大域的なインライン化のみ制御可能であり、呼び出し元固有の制御を行うことはできません。
#pragma inline は、現在のファイル内にある、指定したルーチンに一致する呼び出しをインライン化するようコンパイラに指示します。この指示は、無視されることがあります。たとえば、関数本体が別のモジュールに存在していて、crossfile オプションが使用されていない場合などです。
#pragma no_inline は、現在のファイル内にある、指定したルーチンに一致する呼び出しをインライン化しないようコンパイラに指示します。
次の例に示すように、#pragma inline および #pragma no_inline は、関数がプロトタイプまたは空のパラメータリストで宣言されたあとでのみ使用できます。
static void foo(int); static int bar(int, char *); #pragma inline(foo, bar) |
-xldscope、-xinline、-xO、-xcrossfile も参照してください。
#pragma int_to_unsigned (funcname)
-Xt モードまたは -Xs モードで unsigned の型を返す関数が、戻り値の型 int を持つように変更します。
(SPARC) #pragma MP serial_loop
Sun 固有の MP プラグマは推奨されず、サポートされません。代わりに、OpenMP 3.0 の仕様で規定された API をサポートします。標準命令への移植については、『OpenMP API ユーザーズガイド』を参照してください。
詳細は、「3.8.3.1 直列プラグマ」を参照してください。
(SPARC) #pragma MP serial_loop_nested
Sun 固有の MP プラグマは推奨されず、サポートされません。代わりに、OpenMP 3.0 の仕様で規定された API をサポートします。標準命令への移植については、『Sun Studio: OpenMP API ユーザーズガイド』を参照してください。
詳細は、「3.8.3.1 直列プラグマ」を参照してください。
(SPARC) #pragma MP taskloop
Sun 固有の MP プラグマは推奨されず、サポートされません。代わりに、OpenMP 3.0 の仕様で規定された API をサポートします。標準命令への移植については、『OpenMP API ユーザーズガイド』を参照してください。
詳細は、「3.8.3.2 並列プラグマ」を参照してください。
(SPARC) #pragma nomemorydepend
ループのどの繰り返しでもメモリーの依存がないと指示します。つまり、ループのどの繰り返しの中でも、同じメモリーの参照は必要がないと指示します。このプラグマを指定すると、コンパイラ (パイプライナ) はループの 1 回の繰り返しの中で、より効率的に命令をスケジュールすることができます。ループの繰り返しの中でメモリーの依存があると、プログラムの実行結果は未定義になります。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
このプラグマのスコープは、プラグマから始まり、次のブロックの始まりか現在のブロック内のプラグマに続く最初の for ループ、または現在のブロックの終わりのいずれか先に達したところで終わります。プラグマは、スコープの終端に到達した時点で最初に見つかった for ループに適用されます。
(SPARC) #pragma no_side_effect(funcname[, funcname…])
funcname には、現行の翻訳単位内の関数名を指定します。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。またプラグマはその関数の定義より前に指定されていなければいけません。指定した funcname に対し、プラグマはその関数に一切の副作用がないことを宣言します。つまり、funcname は渡された引数にだけ依存する結果の値を返します。funcname および呼び出された子孫については、次のことがいえます。
呼び出し時点で呼び出し側が認識できるプログラム状態の一部に、読み出しまたは書き込みのためにアクセスすることはありません。
入出力を実行しません。
呼び出し時点で認識できるプログラム状態のどの部分も変更しません。
コンパイラはこの情報を、その関数を用いる最適化に利用することができます。関数に副作用があると、この関数を呼び出すプログラムの実行結果は未定義になります。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
#pragma opt level (funcname[, funcname])
funcname には、現行の翻訳単位内の関数名を指定します。 level の値は、指定した関数に対する最適化レベルです。0、1、2、3、4、5 いずれかの最適化レベルを割り当てることができます。level を 0 に設定すると、最適化を無効にできます。関数は、プラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。プラグマは、最適化する関数の定義を処理する必要があります。
プラグマ内に指定される関数の最適化レベルは、-xmaxopt の値に下げられます。-xmaxopt=off の場合、プラグマは無視されます。
#pragma pack(n)
pack(n) 構造体または共用体のメンバーの境界整列を制御します。デフォルトでは、構造体または共用体のメンバーは、char 型なら 1 バイト、short 型なら 2 バイト、整数なら 4 バイトというように、その自然境界で整列されます。n を指定する場合には、すべての構造体または共用体のメンバーに対してもっとも厳密な自然境界整列となる 2 の乗数にします。ゼロは受け入れられません。
このプラグマを使用すると、構造体または共用体のメンバーの境界整列を指定できます。たとえば、#pragma pack(2) を指定すると、int、long、long long、float、double、long double、pointer が、それぞれの自然境界ではなく、2 バイト境界に整列されます。
n がプラットフォームでもっとも厳密な整列を指示する値 (x86 では 4、SPARC v8 では 8、SPARC v9 では 16) か、それより大きな値の場合は、自然境界整列が有効になります。n が省略された場合も、メンバーは自然境界整列に戻ります。
#pragma pack(n) 指令は、その指令から次の pack 指令の間のすべての構造体または共用体の定義に適用されます。別の翻訳単位で同じ構造体または共用体に対して異なる #pragma pack の定義が行われている場合、プログラムは予期しない形でコンパイルに失敗することがあります。特に、#pragma pack(n) は、事前にコンパイルされたライブラリのインタフェースを定義するヘッダーをインクルードする前には使用しないでください。#pragma pack(n) は、プログラムコード内の境界整列を変更するすべての構造体または共用体の直前に挿入することをお勧めします。そして、その構造体の直後に #pragma pack( ) を続けてください。
#pragma pack を使用する場合、構造体または共用体自身の整列条件は、その構造体または共用体でより厳密に境界整列されるメンバーの整列条件と同一です。したがって、その構造体または共有体の任意の宣言は pack の境界整列となります。たとえば、char 型だけの struct は整列の制限はありませんが、double 型を含む struct は 8 バイトの境界上に並びます。
#pragma pack を使用して構造体または共用体のメンバーを自然境界以外の境界で整列させると、通常、これらのフィールドへのアクセスが発生した場合に SPARC 上でバスエラーが起きます。このエラーを避けるには、-xmemalign オプションも指定します。このようなプログラムをコンパイルする最適な方法については、「B.2.117 -xmemalign=ab」を参照してください。
#pragma pipeloop(n)
このプラグマは、引数 n に正の整定数または 0 を受け入れます。このプラグマは、ループがパイプライン化可能で、ループによる依存の最小の依存距離が n であることを指定します。距離が 0 の場合、そのループは実質的には Fortran 形式の doall ループで、ターゲットプロセッサ上でパイプライン処理するべきであることを意味します。距離が 0 より大きい場合、コンパイラは n 回だけの連続繰り返しでパイプラインを試みます。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
このプラグマのスコープは、プラグマから始まり、次のブロックの始まりか現在のブロック内のプラグマに続く最初の for ループ、または現在のブロックの終わりのいずれか先に達したところで終わります。プラグマは、スコープの終端に到達した時点で最初に見つかった for ループに適用されます。
#pragma rarely_called(funcname[, funcname])
指定した関数があまり使用されないというヒントをコンパイラに与えます。このヒントにより、コンパイラは、プロファイル収集段階に負担をかけることなく、ルーチンの呼び出し元でプロファイルフィードバック方式の最適化を行うことができます。このプラグマはヒントの提示ですので、コンパイラは、このプラグマに基づく最適化を行わないこともあります。
指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。#pragma rarely_called の例を次に示します。
extern void error (char *message); #pragma rarely_called(error) |
#pragma redefine_extname old_extname new_extname
このプラグマにより、オブジェクトコード中で外部定義された old_extname の名前が new_extname に置換されます。この結果、リンク時にリンカーは new_extname だけを認識します。関数定義、初期設定子、または式のいずれかとして old_extname を最初に使用したあと、#pragma redefine_extname が指定されていると、結果は未定義になります。-Xs のモードではこのプラグマはサポートされていません。
#pragma redefine_extname を使用できる場合、コンパイラは、事前に定義されたマクロの __PRAGMA_REDEFINE_EXTNAME の定義を使用して、#pragma redefine_extname の有無に関係なく機能する移植可能なコードを作成できるようにします。
#pragma redefine_extname の目的は、関数名を変更できない場合に関数インタフェースを効率的に再定義する手段を提供することにあります。関数名を変更できない場合とは、たとえば、既存のプログラムとの互換性を保つために、ライブラリ内に、関数の古い定義と、新しいプログラムで使用する 新しい定義の両方を維持する必要がある場合です。ライブラリに新しい名前で新しい関数定義を追加した場合に、このようなことが必要になります。新旧の名前と定義が存在する関数を宣言するヘッダーファイルで #pragma redefine_extname を使用すると、その関数が使用されるときは、必ずその関数の新しい定義でリンクされるようになります。
#if defined(__STDC__) #ifdef __PRAGMA_REDEFINE_EXTNAME extern int myroutine(const long *, int *); #pragma redefine_extname myroutine __fixed_myroutine #else /* __PRAGMA_REDEFINE_EXTNAME */ static int myroutine(const long * arg1, int * arg2) { extern int __myroutine(const long *, int*); return (__myroutine(arg1, arg2)); } #endif /* __PRAGMA_REDEFINE_EXTNAME */ #else /* __STDC__ */ #ifdef __PRAGMA_REDEFINE_EXTNAME extern int myroutine(); #pragma redefine_extname myroutine __fixed_myroutine #else /* __PRAGMA_REDEFINE_EXTNAME */ static int myroutine(arg1, arg2) long *arg1; int *arg2; { extern int __fixed_myroutine(); return (__fixed_myroutine(arg1, arg2)); } #endif /* __PRAGMA_REDEFINE_EXTNAME */ #endif /* __STDC__ */ |
#pragma returns_new_memory (funcname[, funcname])
指定した関数の戻り値が呼び出し元のどのメモリーとも別名処理されないことを表明します。つまり、この呼び出しでは、新しいメモリー位置が返されます。この情報により、オプティマイザはポインタ値をより正確に追跡し、メモリー位置を明確化することができます。この結果、スケジューリング、パイプライン化、ループの並列化が改善されます。表明が偽の場合には、プログラムの動作は未定義になります。
次の例に示すように、このプラグマは、指定した関数をプロトタイプまたは空のパラメータリストで宣言したあとでのみ使用できます。
void *malloc(unsigned); #pragma returns_new_memory(malloc) |
#pragma unknown_control_flow (name;[, name;])
#pragma unknown_control_flow 指令は、呼び出し元のフローグラフを変更する手続きを説明するために使用されます。通常、この指令には setjmp() のような関数の宣言が伴います。Sun のシステム上では、インクルードファイル <setjmp.h> に次の指定が含まれています。
extern int setjmp(); #pragma unknown_control_flow(setjmp) |
setjmp() のような特性を持つほかの関数も、同様に宣言する必要があります。
原則として、この属性を認識するオプティマイザは、制御フローグラフに適切な境界を挿入できます。これによって、setjmp() を呼び出す関数内で関数呼び出しを安全に処理しながら、影響を受けないフローグラフ部分のコードを最適化することが可能になります。
指定した関数は、このプラグマの前にプロトタイプまたは空のパラメータリストで宣言する必要があります。
#pragma unroll (unroll_factor)
このプラグマは、引数 unroll_factor に正の整定数を受け入れます。展開係数が 1 以外の場合、指定されたループを指定の係数で展開するよう、コンパイラに指示します。コンパイラは可能なかぎり、その展開係数を使用します。展開係数が 1 の場合、ループを展開してはいけないことをコンパイラに指定します。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
このプラグマのスコープは、プラグマから始まり、次のブロックの始まりか現在のブロック内のプラグマに続く最初の for ループ、または現在のブロックの終わりのいずれか先に達したところで終わります。プラグマは、スコープの終端に到達した時点で最初に見つかった for ループに適用されます。
#pragma [no_]warn_missing_parameter_info
#pragma warn_missing_parameter_info を指定すると、パラメータ型情報が含まれない関数宣言を持つ関数の呼び出しに対して警告が発生します。次の例を考えてみましょう。
exmaple% cat -n t.c 1 #pragma warn_missing_parameter_info 2 3 int foo(); 4 5 int bar () { 6 7 int i; 8 9 i = foo(i); 10 11 return i; 12 } % cc t.c -c -errtags "t.c", line 9: warning: function foo has no prototype (E_NO_MISSED_PARAMS_ALLOWED) example% |
#pragma no_warn_missing_parameter_info は、それ以前の #pragma warn_missing_parameter_info を無効にします。
デフォルトでは、#pragma no_warn_missing_parameter_infoは有効です。
#pragma weak symbol1 [= symbol2]
弱い大域シンボルを定義します。このプラグマは主にソースファイルの中でライブラリを構築するために使用されます。リンカーは弱いシンボルを解決できなくてもエラーメッセージを表示しません。
#pragma weak symbol |
これは symbol を弱いシンボルとして定義しています。symbol の定義が見つからなくても、リンカーはメッセージ等を出さなくなります。
#pragma weak symbol1 = symbol2 |
これは symbol1 を、symbol2 の別名である弱いシンボルと定義します。この形式のプラグマは、ソースファイルまたはそこにインクルードされたヘッダーファイルのいずれかで、symbol2 を定義した同じ変換ユニットの中にかぎり使用できます。それ以外で使用された場合は、コンパイルエラーになります。
プログラムが symbol1 を呼び出しますが、それがプログラム中で定義されていない場合には、symbol1 がリンクライブラリ中の弱いシンボルになっていると、リンカーはライブラリにある定義を使用します。しかし、プログラム自身が symbol1 を定義してある場合、プログラムでの定義が優先され、ライブラリに存在する symbol1 の弱い大域定義は使用されません。プログラムが直接 symbol2 を呼び出すと、ライブラリにある定義が使用されます。symbol2 の定義が重複して行われるとエラーになります。
次の識別子は、オブジェクトに似たマクロとして事前に定義されています。
表 2–3 事前定義済みの識別子
識別子 |
内容の説明 |
__STDC__ |
__STDC__ 1 -Xc __STDC__ 0 -Xa, -Xt 未定義 -Xs |
__STDC__ が未定義 (#undef __STDC__) の場合、警告が発行されます。__STDC__ は、-Xs モードでは定義されていません。
事前定義に関する現在のリストは、cc(1) のマニュアルページを参照してください。
-fast を指定した場合は、errno の値を使用しないでください。この問題を回避するもっとも簡単な方法は、-fast を使用しないことです。
-fast を指定して、かつ errno の値が必要な場合は、次のことを行なってください。
数学最適化ライブラリとのリンクでは、-lmopt を指定しない。
-xbuiltin=none、-U__MATHERR_ERRNO_DONTCARE、-xnolibmopt、および -xnolibmil を指定する
C コンパイラは、C 言語に多数の拡張機能を実装しています。
C コンパイラは、C99 規格の restrict キーワードの同義語として _Restrict キーワードをサポートします。restrict キーワードは、-xc99=all を指定する場合にしか使用できませんが、_Restrict キーワードは、-xc99=none と -xc99=all のどちらを指定する場合でも使用できます。
サポートしている C99 機能の詳細は、表 C–6 を参照してください。
__asm キーワード (先頭の 2 つの下線に注意) は、asm キーワードと同義語です。__asm キーワードではなく asm を使用し、-Xc モードでコンパイルを実行する場合、コンパイラは警告メッセージを表示します。-Xc モードで _ _asm を使用する場合、警告メッセージは表示されません。_ _asm 文の書式は次のようになります。
__asm("string"); |
ここで、string は有効なアセンブリ言語文です。
この文は、与えられたアセンブラテキストをアセンブリファイルに直接出力します。関数スコープではなくファイルスコープで宣言された基本的な asm 文は、「グローバル asm 文」と呼びます。ほかのコンパイラはこの文を「トップレベル」asm 文と呼びます。
グローバル asm 文は、指定された順に出力されます。つまり、互いに対して相対的な順序を維持し、周囲の関数との相対的な位置を維持します。
より高い最適化レベルでは、参照されていないと考えられる関数をコンパイラが削除することがあります。グローバル asm 内ではどの関数が参照されているかをコンパイラが把握しないので、関数が誤って削除される可能性があります。
テンプレートとオペランドの仕様を提供する、拡張された asm 文は、グローバルにできないことに注意してください。__asm および __asm__ はキーワード asm の同義語であり、互いに入れ替えて使用できます。
__inline および __inline__ はキーワード inline の同義語であり、互いに入れ替えて使用できます (C 規格、6.4.1 節)。
__builtin_constant_p はコンパイラの組み込み関数です。この関数は単一の数値引数を取り、引数がコンパイル時の定数として既知である場合は 1 を返します。戻り値 0 は、その引数がコンパイル時の定数であるかどうかをコンパイラが判定できないことを意味します。この組み込み関数の標準的な使用法は、マクロ内で手動のコンパイル時最適化を行うことです。
__FUNCTION__ と __PRETTY_FUNCTION__ は定義済みの識別子であり、字句を包含する関数の名前が含まれています。これらは、c99 の定義済みの識別子である __func__ に相当する機能です。Solaris プラットフォームでは、__FUNCTION__ と __PRETTY_FUNCTION__ は -Xs および -Xc の各モードで使用できません。
ここでは、コンパイルや実行時環境を制御する環境変数について説明します。
プログラムをマルチプロセッサ上で実行する場合に、使用するプロセッサ の数を指定します。対象マシンに複数のプロセッサが搭載されている場合は、スレッドは個々のプロセッサにマップできます。この例では、プログラムを実行すると、2 個のスレッドが生成され、各スレッド上でプログラムの並列化された部分が実行されるようになります。
-xprofile=collect コマンドが実行頻度のデータを格納しているファイルの名前を制御します。
-xprofile=collect コマンドが実行頻度データファイルを配置するディレクトリを制御します。
各ヘルパースレッドのタスク終了状態を制御します。 spin ns、または sleep nms と設定できます。デフォルトは sleep です。詳細については、『OpenMP API ユーザーズガイド』を参照してください。
cc は通常 /tmp ディレクトリに一時ファイルを作成します。環境変数 TMPDIR を設定すると、別のディレクトリを指定することができます。TMPDIR が有効なディレクトリ名でない場合は、/tmp が使用されます。-xtemp オプションと環境変数 TMPDIR では、-xtemp が優先されます。
Bourne シェルの場合は次のように入力します。
$ TMPDIR=dir; export TMPDIR |
C シェルの場合は次のように入力します。
% setenv TMPDIR dir |
インクルードC コンパイラシステムに付属している標準的なヘッダーファイルのいずれかをインクルードするには、次の形式を使用します。
#include <stdio.h> |
山括弧 (<>) を使用すると、プリプロセッサがが、システム内にあるヘッダーファイル用の標準的な場所でヘッダーファイルを検索するようになります。通常は /usr/include ディレクトリです。
ユーザーが自分のディレクトリに格納したヘッダーファイルの場合は、次のように書式が異なります。
#include "header.h" |
書式 #include "foo.h" (二重引用符を使用) の文に対し、コンパイラは、次の順番でインクルードファイルを検索します。
現在のディレクトリ (つまり、「インクルード」するファイルを含むディレクトリ)
-I オプションで命名されたディレクトリ
/usr/include ディレクトリ
ヘッダーファイルがインクルードされたソースファイルと同じディレクトリにない場合は、cc コマンドで -I オプションを使用して、ヘッダーファイルが格納されているディレクトリのパスを指定してください。たとえば次のように、ソースファイル mycode.c の中で stdio.h と header.h をインクルードしたとします。
#include <stdio.h> #include "header.h" |
この header.h が ../defs ディレクトリに格納されている場合は、次のコマンドを実行します。次のようなコマンド行があるとします。
% cc– I../defs mycode.c |
この場合、プリプロセッサが header.h を検索する順序は、最初が mycode.c を含むディレクトリ、次が ../defs ディレクトリ、最後が標準の場所となります。stdio.h については最初が ../defs、次が標準の場所となります。相違点は、現ディレクトリを検索するのは名前を二重引用符で囲んだヘッダーファイルを検索する場合だけであることです。
-I オプションは 1 つの cc コマンド行の中で複数回指定することができます。指定したディレクトリをプリプロセッサが検索する順序は、コマンド行での指定順序と同じです。cc の複数のオプションを、同じコマンド行で指定できます。
% cc– o prog– I../defs mycode.c |
新しい -I- オプションは、デフォルトの検索規則に対する制御をさらに強化します。この節で説明しているように、コマンド行の最初の -I- だけが有効です。-I- オプションをコマンド行に配置すると、次のような効果があります。
#include "foo.h" 形式のインクルードファイルの場合、次の順序でディレクトリを検索します。
1. -I オプションで指定されたディレクトリ内 (-I- の前後)
2. コンパイラで提供される C++ ヘッダーファイル、ANSI C ヘッダーファイル、および特殊な目的のファイルのディレクトリ
3. /usr/include ディレクトリ内
#include <foo.h> 形式のインクルードファイルの場合、次の順序でディレクトリを検索します。
1.-I のあとに指定した -I- オプションで指定したディレクトリ内。
2. コンパイラで提供される C++ ヘッダーファイル、ANSI C ヘッダーファイル、および特殊な目的のファイルのディレクトリ。
3. /usr/include ディレクトリ内。
次の例は、prog.c のコンパイル時に -I- を使用した結果を示しています。
prog.c #include "a.h" #include <b.h> #include "c.h" c.h #ifndef _C_H_1 #define _C_H_1 int c1; #endif int/a.h #ifndef _A_H #define _A_H #include "c.h" int a; #endif int/b.h #ifndef _B_H #define _B_H #include <c.h> int b; #endif int/c.h #ifndef _C_H_2 #define _C_H_2 int c2; #endif |
次のコマンドでは、#include "foo.h" 形式のインクルード文のカレントディレクトリ (インクルードしているファイルのディレクトリ) のデフォルトの検索動作を示します。inc/a.h の #include "c.h" 文を処理する際、プリプロセッサは、inc サブディレクトリから c.h ヘッダーファイルを読み取ります。prog.c の #include "c.h" 文を処理する際、プリプロセッサは、prog.c を含むディレクトリから c.h ファイルを取り込みます。-H オプションがインクルードファイルのパスを印刷するようにコンパイラに指示していることに注意してください。
example% cc -c -Iinc -H prog.c inc/a.h inc/c.h inc/b.h inc/c.h c.h |
次のコマンドでは、-I- オプションの影響を示します。プリプロセッサは、書式 #include "foo.h" の文を処理する際に、インクルードするディレクトリを最初に検索しません。代わりに、コマンド行に配置されている順番で、-I オプションで命名されたディレクトリを検索します。inc/a.h 内にある #include "c.h" 文を処理する際、プリプロセッサは /c.h ヘッダーファイルを、inc/c.h ヘッダーファイルの代わりに取り込みます。
example% cc -c -I. -I- -Iinc -H prog.c inc/a.h ./c.h inc/b.h inc/c.h ./c.h |
コンパイラがインストールされている位置の /usr/include、 /lib、/usr/lib を検索ディレクトリに指定しないでください。
詳細は、「B.2.37 -I[-|dir]」の節を参照してください。