| C ユーザーズガイド |
第 3 章
Sun ANSI/ISO C コンパイラに固有の情報
Sun ANSI/ISO C コンパイラは、プログラミング言語用米国標準規格 C、ANSI/ISO 9899-1990 に記述されている C 言語と互換性があります。この章では、Sun ANSI/ISO C コンパイラに固有の部分を説明します。
環境変数
TMPDIR
ccは通常/tmpディレクトリに一時ファイルを作成します。環境変数TMPDIRを設定すると、別のディレクトリを指定することができます。TMPDIRが有効なディレクトリ名でない場合は、/tmpが使用されます。-xtempオプションと環境変数TMPDIRでは、-xtempが優先されます。
$ TMPDIR=<ディレクトリ>; export TMPDIR
% setenv TMPDIR<ディレクトリ>
SUNPRO_SB_INIT_FILE_NAME
.sbinit(5)ファイルが格納されるディレクトリの絶対パス名。この変数は、-xsbまたは-xsbfastフラグが使用されている場合にのみ使用されます。
PARALLEL(SPARC) プログラムをマルチプロセッサ上で実行する場合に、使用するプロセッサの数を指定します。対象マシンに複数のプロセッサが搭載されている場合、スレッドは個々のプロセッサにマップできます。プログラムを実行すると、指定したプロセッサ数のスレッドが生成され、並列化されたプログラムの各部が実行されます。
SUNW_MP_THR_IDLE並列ジョブの分担部分を終えた後の各スレッドの状態を制御します。
SUNW_MP_THR_IDLEは、spinまたはsleep[ns|nms]に設定できます。デフォルトはspinであり、これはスレッドがスピン待ちに入ることを意味します。もう一方の選択肢、sleep[ns|nms]は、n に指定された時間だけスレッドをスピン待ち状態にし、その後休眠させることを意味します。待ち時間の単位は秒 (s:デフォルトの単位) かミリ秒 (ms) で、1sは 1 秒、10msは 10 ミリ秒を意味します。n に指定された時間が経過する前に新しいジョブが発生すると、スレッドはスピン待ちを中止し、新しいジョブを開始します。SUNW_MP_THR_IDLEに無効な値が含まれるか、あるいはこのオプションが指定されない場合、デフォルトとしてspinが使用されます。大域動作: 値保存と符号なし保存
符号なし保存算術変換に依存しているプログラムの動作は以前とは異なっています。これは、K&R C と ANSI/ISO C の最も大きな違いです。
K&R による『プログラミング言語 C』(共立出版、1978 年) の中では、
unsignedは型を正確に 1 つだけ指定していました。そのときはunsigned char、unsigned short、unsigned longはありませんでしたが、その後間もなくほとんどの C コンパイラがこれらを追加しました。K&R C コンパイラでは、符号なし保存規則が拡張 (promotion) のときに使用されます。すなわち、符号なし型が拡大を必要とするときは符号なし型に拡大され、符号なし型が符号付き型と混合されると結果として符号なし型になります。
これに対して ANSI/ISO C の規則は、値保存と呼ばれています。この保存規則では、結果の型はオペランドの型のサイズ間の関係によって決定されます。
unsigned charまたはunsigned shortが拡張されると、結果の型はintの大きさがその値全部を表現するのに十分なものであればintです。それ以外の場合、結果の型はunsigned intです。値保存規則は、ほとんどの式で最も妥当な算術結果となります。コンパイラは、
-Xtモードと-Xsモードでだけ符号なし保存拡張規則を使用します。他のモード、すなわち-Xcモードと-Xaモードでは、値保存拡張規則が使用されます。-xtransitionオプションを使用した場合は、使用される拡張規則によって動作が異なる可能性のある式があれば、コンパイラは式ごとに警告を発します。キーワード
asm予約語
asmは、-Xcを除くすべてのコンパイラモードで予約されています。予約語_asmはasmの同義語で、すべてのコンパイラモードの下で使用できますが、-Xcモードで使用した場合は警告が出されます。asm文の形式は次のとおりです。
asm("<文字列>"):
ここで、<文字列> は有効なアセンブリ言語文です。
asm文は関数の本体内部に指定しなければなりません。
_Restrictコンパイラが効果的にループの並列実行を行うには、ある種の左辺値 (
lvalue) が異なる記憶領域を指しているかどうかを判別する必要があります。別名とは、記憶領域が異なっていない左辺値のことです。オブジェクトを示す 2 つのポインタが別名かどうかを判別するのは、プログラム全体の解析を必要とすることがあるため、難しく時間のかかる作業です。
void vsq(int n, double * a, double * b){int i;for (i=0; i<n; i++) b[i] = a[i] * a[i];}
コンパイラは、ポインタ
aとbが異なるオブジェクトにアクセスすることがわかっていれば 、ループの異なる繰り返しの実行を並列化することができます。ポインタaとbでアクセスされるオブジェクトが重なりあっている場合には、コンパイラが並列でループを実行するのは安全ではありません。コンパイル時には、コンパイラは関数vsq() を単純に解析しただけではaとbでアクセスするオブジェクトが重なり合っているかどうかがわかりません。この情報を得るにはプログラム全体の解析が必要です。コンパイラがポインタ別名解析を実行できるよう、制限付きポインタを使用して異なるオ ブジェクトを示すポインタを指定します。制限付きポインタをサポートするために、キーワード
_Restrictが Sun ANSI/ISO C コンパイラによって拡張として認識されます。次にvsq() の関数引数を制限付きポインタとして宣言する例を示します。
void vsq(int n, double * _Restrict a, double * _Restrict b)
ポインタ
aとbが制限付きポインタとして宣言されると、コンパイラはaとbが示す記憶域の領域が異なるものだと認識します。この別名情報により、コンパイラはループを並列化することができます。
_Restrictキーワードはvolatileと同じような型修飾子で、ポインタ型だけを修飾します。_Restrictは、コンパイルモード-Xa(デフォルト) と-Xtでのみキーワードとして認識されます。これらの 2 つのモードでは、コンパイラはマクロ__RESTRICTを定義して、ユーザーが制限付きポインタを用いて移植可能なコードを書けるようにします。コンパイラはマクロ
__RESTRICTを定義して、ユーザーが制限付きポインタを用いて移植可能なコードを書けるようにします。たとえば、次のコードは Sun ANSI/ISO C コンパイラのすべてのコンパイルモードで使用でき、制限付きポインタをサポートしていない他のコンパイラでも使用できます。
#ifdef __RESTRICT#define restrict _Restrict#else#define restrict#endifvoid vsq(int n, double * restrict a, double * restrict b){int i;for (i=0; i<n; i++) b[i] = a[i] * a[i];}
制限付きポインタが ANSI/ISO C 標準の一部に加わると、"
restrict" がキーワードになるものと思われます。ユーザーがvsq() でのように制限付きポインタを用いてコードを書く場合の一例を以下に示します。
#define restrict _Restrict
こうしておけば、ANSI/ISO C で
restrictがキーワードになっても、変更を最小限にとどめることができます。Sun ANSI/ISO C コンパイラではキーワードとして_Restrictを使用しています。これが処理系の名前空間に入っていて、ユーザーの名前空間にある識別子と衝突しないためです。ユーザーがソースコードを変更したくない場合があります。その場合、コマンド行オプション
-xrestrictを用いて、ポインタ値のある関数引数を制限付きポインタとして扱うよう指定することができます。詳細は、「-xrestrict=f」を参照してください。関数リストを指定すると、指定した関数内のポインタ引数は制限付きポインタとして扱われます。関数リストを指定しない場合は、C ファイル全体のすべてのポインタ引数が制限付きとして扱われます。たとえば、
-xrestrict=vsqは、関数vsq() の例で示したポインタaとbをキーワード_Restrictで修飾します。
_Restrictは正しく使用することが重要です。制限付きポインタとして修飾されているポインタが、同一オブジェクトを指していると、ループは誤って並列化されることがあり、未定義の動作が発生します。たとえば、関数vsq() のポインタaとbが重なり合ったオブジェクトを指し、b[i]とa[i+1]は同じオブジェクトであるとします。aとbが制限付きポインタとして宣言されなければ、ループは順次に実行されます。aとbが誤って制限付きポインタとして宣言されると、コンパイラはループの実行を並列化することがあります。b[i+1]の計算は、b[i]の計算後に実行される必要が生じるため、これは安全とは言えません。
long longデータ型Sun ANSI/ISO C コンパイラにはデータ型
long longおよびunsigned long longがあり、これらはデータ型longと類似しています。longには 32 ビットの情報を格納できるのに対し、longlongには 64 ビットの情報を格納できます。long longは-Xcモードでは使用できません。
long longデータ型の入出力
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に変換されます。- 一方のオペランドが
long int型を持ち、もう一方がunsigned int型を持つ場合、両オペランドはunsigned long intに変換されます。- 一方のオペランドが
long int型を持つ場合、もう一方のオペランドはlong intに変換されます。- 一方のオペランドが
unsigned int型を持つ場合、もう一方のオペランドはunsigned intに変換されます。- これ以外の場合、両オペランドは
int型になります。定数
ここでは、Sun ANSI/ISO C コンパイラに固有の定数に関する情報について説明します。
整数定数
下表に示すように、10 進数、8 進数、16 進数の定数に接尾辞を付けて型を示すことができます。
表 3-1 データ型の接尾辞 uまたはUunsignedlまたはLlongllまたはLLlong long1lu、LU、Lu、lU、ul、uL、Ul、ULのいずれかunsigned longllu、LLU、LLu、llU、ull、ULL、uLL、Ullのいずれかunsigned long long2
1 long longは -Xc モードでは使用できません。 2unsigned long longは -Xc モードでは使用できません。
コンパイラが接尾辞を持たない定数の型を割り当てる場合、定数の大きさに応じて、次のリストから値が表現できる最初の型を使用します。
文字定数
エスケープシーケンスではない複数の文字からなる文字定数は、各文字が持つ数値から派生する値を持ちます。たとえば定数 '
123' の持つ値は以下のようになります。
0' 3'' 2'' 1'
-Xsオプション使用の場合、あるいは ANSI/ISO でない他の C では、この値は以下のようになります。
0' 1'' 2'' 3'
インクルードファイル
Cコンパイルシステムに含まれる標準ヘッダーファイルをインクルードするには、次の書式を使用します。
#include <stdio.h>
山括弧(
<>)を使用するとプリプロセッサはシステム内の標準の場所、通常は/usr/includeディレクトリにあるヘッダーファイルを検索します。ユーザーが自分のディレクトリに格納したヘッダーファイルの場合は、次のように書式が異なります。
#include "header.h"
二重引用符 (
"") を使用すると、プリプロセッサはまず、この #include行を含むファイルが格納されているディレクトリの中でheader.hを検索します。ヘッダーファイルがインクルードされたソースファイルと同じディレクトリにない場合は、
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非標準浮動小数点
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-391.2345e-40と続いていきます。これがデフォルト動作である「段階的アンダーフロー」です。非標準モードでは、仮数部から「盗む」ことをせず、通常は単に
xをゼロに設定します。前処理指令と名前
ここでは、表明、プラグマ、および事前定義名について説明します。
表明 (assertion)
#assert<述語> (<トークン列>)<トークン列> は、表明の名前領域 (マクロ定義用の領域から分離されています) にある <述語> と関連付けられます。<述語> は識別子トークンでなければなりません。
#assert <述語>これは <述語> が存在していることを表明しますが、それにトークン列を関連付けることはしません (
-Xcモードを除く)。コンパイラは、次のような事前定義された述語をデフォルトとして提供しています。
#assert system (unix)#assert machine (sparc)(SPARC)#assert machine (i386)(x86)#assert cpu (sparc)(SPARC)#assert cpu (i386)(x86)
lintは、次のような事前定義された述語をデフォルトとして提供しています (-Xcモードを除く)。
#assert lint (on)
表明は
#unassertを使用して削除できます。この場合、#assertと同じ構文が使用されます。引数なしで#unassertを使用すると述語に対するすべての表明が削除され、表明を指定すればその表明だけが削除されます。表明は、次の構文を持つ
#if文でテストすることができます。
#if #<述語>(<空でないトークン列>)たとえば以下のように指定して、事前定義された述語
systemをテストすることができます。
#if #system(unix)プラグマ
以下の書式を持つ前処理行は、各処理系が定義した処理を指定します。
#pragma <プリプロセッサトークン>次の
#pragmaはコンパイルシステムに認識されます。認識されなかったプラグマは無視されます。-vオプションを使用すると、認識されなかったプラグマについて警告が出されます。
#pragma align<整数>(<変数>[,<変数>])
- 整列プラグマで指定した <変数> のメモリーはデフォルト値によらず、すべて <整数> バイト境界に揃えられます。
- <整数> には 2 の階乗 (1 〜 128) を指定します。有効な値は 1、2、4、8、16、32、64、128 です。
- <変数> には大域または静的な変数を指定します。自動変数は指定できません。
- 指定された境界がデフォルトより小さい場合は、デフォルトが優先します。
- プラグマ行は、その行に指定される変数の宣言よりも先になければなりません。先にないと無視されてしまいます。
- プラグマ行で記述されているが、その後で宣言されていない変数は無視されます。たとえば次のようになります。
#pragma align 64 (aninteger, astring, astruct)int aninteger;static char astring[256];struct astruct{int a; char *b;};
#pragma does_not_read_global_data(<関数>[,<関数>])リストに指定したルーチンが直接にも間接にも大域データを読み取らないことを表明します。この表明により、そうしたルーチンへの呼び出し前後のコードをさらに最適化することができます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。
このプラグマは、指定した関数のプロトタイプを宣言した後でのみ使用できます。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。
#pragma does_not_return(<関数>[,<関数>])指定した関数への呼び出しが復帰しないことをコンパイラのバックエンドに表明します。この表明により、オプティマイザは、指定された関数への呼び出しが戻らないと仮定して最適化を行うことができます。たとえば、レジスタの存続期間が呼び出し元で終了する場合は、さらに最適化率を高めることができます。
指定した関数が復帰した場合は、プログラムの動作は未定義になります。
次の例に示すように、このプラグマは、指定した関数のプロトタイプを宣言した後でのみ使用できます。
extern void exit(int);#pragma does_note_return(exit);extern void __assert(int);#pragma does_not_return(__assert);
#pragma does_not_write_global_data(<関数>[,<関数>])リストに指定したルーチンが直接にも間接にも大域データを書き込まないことを表明します。この表明により、そうしたルーチンへの呼び出し前後のコードをさらに最適化することができます。具体的には、代入文やストア命令をそうした呼び出しの前後に移動することができます。
このプラグマは、指定した関数のプロトタイプを宣言した後でのみ使用できます。大域アクセスに関する表明が真でない場合は、プログラムの動作は未定義になります。
#pragma error_messages(on|off|default,<タグ>... <タグ>)このエラーメッセージプラグマは、ソースプログラムの中から、C コンパイラおよび lint が発行するメッセージを制御可能にします。C コンパイラでは、警告メッセージに対してのみ有効です。C コンパイラの -w オプションを使用すると、このプラグマは無効になり、すべての警告メッセージが抑止されます。
#pragmaerror_messages(on, <タグ>... <タグ>)#pragmaerror_messages(off, <タグ>... <タグ>)
- off オプションは、C コンパイラまたは lint プログラムが指定トークンから始まる特定のメッセージを発行することを禁止します。この特定のエラーメッセージに対するプラグマの指定は、別の
#pragma error_messagesによって無効にされるか、コンパイルが終了するまで有効です。#pragmaerror_messages(default, <タグ>... <タグ>)
#pragma fini(<関数 1>[,<関数 2>...,<関数 n>])
main()ルーチンを呼び出した後、<関数 1> から <関数 n> までの関数 (終了関数) を呼び出します。この種の関数は、型がvoidで引数はあってはなりません。プログラム制御下でプログラムが終了したとき、またはこのプログラムを含む共有オブジェクトがメモリーから除去されたときに呼び出されます。次の「初期化関数」の場合と同様、終了関数もリンクエディタによって処理された順に実行されます。
#pragma ident<文字列>
- 実行可能プログラムの
.commentセクション内に任意の <文字列> を格納します。
#pragma init(<関数 1>[,<関数 2>...,<関数 n>])
main()を呼び出す前に、<関数 1> から <関数 n> までの関数 (初期化関数) を呼び出します。この種の関数は、型がvoidで引数はあってはなりません。実行開始時にプログラムのメモリーイメージを構成しているときに呼び出されます。共有オブジェクトの中の初期値設定子は、その共有オブジェクトをメモリー内へ持っていく動作の間、つまりプログラムの開始中、またはdlopen()のような動的ロード動作中に実行されます。初期化関数の呼び出しを順序付ける方法は、それがリンクエディタによって動的または静的に処理される順序に依存します。
#pragma[no_]inline(関数[,関数])指定したルーチン名のインライン化を制御します。このプラグマはファイル全体に対して有効です。このプラグマでは、大域的なインライン化のみ制御可能であり、呼び出し元固有の制御を行うことはできません。
#pragmainlineは、現在のファイル内にある、指定したルーチンに一致する呼び出しをインライン化するようコンパイラに指示します。この指示は、無視されることがあります。たとえば、関数本体が別のモジュールに存在していて、-xcrossfileオプションが使用されていない場合などです。
#pragmano_inlineは、現在のファイル内にある、指定したルーチンに一致する呼び出しをインライン化しないようコンパイラに指示します。次の例に示すように、
#pragmainlineおよび#pragmano_inlineは、指定した関数のプロトタイプを宣言した後でのみ使用できます。
static void foo(int);static int bar(int, char *);#pragma inline(foo, bar);
#pragma int_to_unsigned(<関数>)
-Xtモードまたは-Xsモードでunsignedの型を返す <関数> が、戻り値の型intを持つように変更します。(SPARC)
#pragma MP serial_loop
- 詳細については 「直列プラグマ」を参照してください。
(SPARC)
#pragma MP serial_loop_nested
- 詳細については 「直列プラグマ」を参照してください。
(SPARC)
#pragma MP taskloop
- 詳細については 「直列プラグマ」を参照してください。
(SPARC)
#pragma nomemorydepend
- ループのどの繰り返しでもメモリーの依存がないと指示します。つまり、ループのどの繰り返しの中でも、同じメモリーの参照は必要がないと指示します。このプラグマを指定すると、コンパイラ (パイプライナ) はループの 1 回の繰り返しの中で、より効率的に命令をスケジュールすることができます。ループの繰り返しの中でメモリーの依存があると、プログラムの実行結果は未定義になります。プラグマは現行ブロック内の次の
forループに適用されます。コンパイラはこの情報をレベル 3 以上の最適化に利用します。(SPARC)
#pragma no_side_effect(<関数>[,<関数>])<関数> には、現行の翻訳単位内の関数名を指定します。関数はプラグマより前に宣言されていなければなりません。またプラグマはその関数の定義より前に指定されていなければなりません。指定した <関数> に対し、プラグマはその関数に一切の副作用がないことを宣言します。つまり、<関数> は渡された引数にだけ依存する結果の値を返します。<関数> および呼び出された子孫については、次のことがいえます。
- 呼び出し時点で呼び出し側が認識できるプログラム状態の一部に、読み出しまたは書き込みのためにアクセスすることはありません。
- 入出力を実行しません。
- 呼び出し時点で認識できるプログラム状態のどの部分も変更しません。
コンパイラはこの情報を、その関数を用いる最適化に利用することができます。関数に副作用があると、この関数を呼び出すプログラムの実行結果 は未定義になります。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
#pragmaoptlevel (<関数>[,<関数>])指定した関数サブプログラムに対する最適化レベルを指定します。最適化レベルとしては 0〜5 を選択することができます。0 に設定すると、最適化は無効になります。このプラグマを使用する前に、関数サブプログラムのプロトタイプを作成しておく必要があります。
プラグマ内に指定される関数の最適化レベルは、
-xmaxoptの値に下げられます。-xmaxopt=offの場合、プラグマは無視されます。
#pragmapack(n)
- 構造体のメンバーの境界整列を制御します。デフォルトでは、構造体のメンバーは、
char型なら 1 バイト、short型なら 2 バイト、整数なら 4 バイトというように、その自然境界で整列させられます。n を指定する場合には、すべての構造体メンバーに対して最も厳密な自然境界整列となる 2 の乗数にします。ゼロは受け入れられません。- このプラグマを使用すると、構造体メンバーの境界整列を指定できます。たとえば、
#pragmapack(2)を指定すると、int、long、longlong、float、double、longdouble、pointerが、それぞれの自然境界ではなく、2 バイト境界に整列されます。n がプラットフォームで最も厳密な整列を指示する値 (Intel では 4、SPARC v8 では 8、SPARC v9 では 16) か、それより大きな値の場合は、自然境界整列が有効になります。n が省略された場合も、メンバーは自然境界整列に戻ります。
#pragmapack(n)指令は、その指令から次の#pragmapack指令の間のすべての構造体の定義に適用されます。別の翻訳単位で同じ構造体に対して異なる#pragmapackの定義が行われている場合、プログラムは予期しない形でコンパイルに失敗することがあります。特に、#pragmapack(n)は、事前にコンパイルされたライブラリのインタフェースを定義するヘッダーをインクルードする前には使用しないでください。#pragmapack(n)は、プログラムコード内の境界整列を変更するすべての構造体の直前に挿入することをお勧めします。そして、その構造体の直後に#pragma pack()を続けてください。
注 -#pragmapackを使用して構造体メンバーを自然境界以外の境界で整列させると、これらのフィールドへのアクセスが発生した場合に SPARC 上でバスエラーが起きることがあります。このようなプログラムをコンパイルする最適な方法については、「-xmemalign=ab」を参照してください。
(SPARC)
#pragma pipeloop(n)このプラグマは、引数 n に正の整定数または 0 を受け入れます。このプラグマは、ループがパイプライン化可能で、ループによる依存の最小の依存距離が n であることを指定します。距離が 0 の場合、そのループは実質的には Fortran 形式の
doallループで、ターゲットプロセッサ上でパイプライン処理するべきであることを意味します。距離が 0 より大きい場合、コンパイラは n 回だけの連続繰り返しでパイプラインを試みます。プラグマは現行ブロック内の次のforループに適用されます。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
#pragma rarely_called(<関数>[,<関数>])指定した関数があまり使用されないというヒントをコンパイラのバックエンドに与えます。このヒントにより、コンパイラは、プロファイル収集段階に負担をかけることなく、ルーチンの呼び出し元でプロファイルフィードバック方式の最適化を行うことができます。このプラグマは提案ですので、コンパイラのオプティマイザは、このプラグマに基づく最適化を行わないこともあります。
次の例に示すように、
#pragma rarely_calledプリプロセッサ指令は、指定した関数のプロトタイプを宣言した後でのみ使用できます。
extern void error (char *message);#pragma rarely_called(error);
#pragma redefine_extname<旧外部参照名> <新外部参照名>このプラグマにより、オブジェクトコード中で外部定義された <旧外部参照名> の名前がすべて <新外部参照名> に置換されます。この結果、リンク時にリンカーは新しい名前だけを認識します。関数定義、初期設定子、または式のいずれかとして
<旧外部参照名> を最初に使用した後、#pragma redefine_extname が指定されていると、結果は未定義になります (-Xsのモードではこのプラグマはサポートされていません)。
#pragma redefine_extnameを使用できる場合、コンパイラは、事前に定義されたマクロのPRAGMA_REDEFINE_EXTNAMEの定義を使用して、#pragma redefine_extnameの有無に関係なく機能する移植可能なコードを作成できるようにします。
#pragma redefine_extnameの目的は、関数名を変更できない場合に関数インタフェースを効率的に再定義する手段を提供することにあります。関数名を変更できない場合とは、たとえば、既存のプログラムとの互換性を保つために、ライブラリ内に、関数の古い定義と (新しいプログラムで使用する) 新しい定義の両方を維持する必要がある場合です。ライブラリに新しい名前で新しい関数定義を追加した場合に、このようなことが必要になります。新旧の名前と定義が存在する関数を宣言するヘッダーファイルで#pragma redefine_extnameを使用すると、その関数が使用されるときは、必ずその関数の新しい定義でリンクされるようになります。
#pragma returns_new_memory(<関数>[,<関数>])指定した関数の戻り値が呼び出し元のどのメモリーとも別名処理されないことを表明します。つまり、この呼び出しでは、新しいメモリー位置が返されます。この表明により、オプティマイザはポインタ値を追跡して、メモリー位置を明確にし、ループのスケジューリングやパイプライン処理、並列化処理を改善することができます。表明が偽の場合には、プログラムの動作は未定義になります。
次の例に示すように、このプラグマは、指定した関数のプロトタイプを宣言した後でのみ使用できます。
void *malloc(unsigned);#pragma returns_new_memory(malloc);
#pragma unknown_control_flow (<名前>[,<名前>]...)
#pragma unknown_control_flow指令は、呼び出し元のフローグラフを変更する手続きを説明するために使用されます。通常、この指令にはsetjmp()のような関数の宣言が伴います。サンのシステム上では、インクルードファイル<setjmp.h>に次の指定が含まれています。
extern int setjmp();#pragma unkown_control_flow(setjmp);
setjmp()のような特性を持つ他の関数も、同様に宣言する必要があります。原則として、この属性を認識するオプティマイザは、制御フローグラフに適切な境界を挿入できます。これによって、
setjmp()を呼び出す関数内で関数呼び出しを安全に処理しながら、影響を受けないフローグラフ部分のコードを最適化することが可能になります。(SPARC)
#pragma unroll(<展開係数>)このプラグマは、引数 <展開係数> に正の整定数を受け入れます。プラグマは現行ブロック内の次の
forループに適用されます。展開係数が 1 以外の場合、指定されたループを指定の係数で展開するよう、コンパイラに指示します 。コンパイラは可能な限り、その展開係数を使用します。展開係数が 1 の場合、ループを展開してはならないことをコンパイラに指定します。コンパイラはこの情報をレベル 3 以上の最適化に利用します。
#pragma weak<シンボル 1> [=<シンボル 2>]
弱い大域シンボルを定義します。このプラグマは主にライブラリを構築するソースファイルの中で使用されます。リンカーは弱いシンボルを解決できなくてもエラーメッセージを表示しません。
#pragma weak <シンボル>
- これは <シンボル 1> を、<シンボル 2> の別名の弱いシンボルと定義します。この形式のプラグマは、ソースファイルまたはそこにインクルードされたヘッダーファイルのいずれかで、<シンボル 2> を定義した同じ変換ユニットの中に限り使用できます。それ以外で使用された場合は、コンパイルエラーになります。
- プログラムが <シンボル 1> を呼び出しますが、それがプログラム中で定義されていない場合には、<シンボル 1> がリンクライブラリ中の弱いシンボルになっていると、リンカーはライブラリにある定義を使用します。しかし、プログラム自身が <シンボル 1> を定義してある場合、プログラムでの定義が優先され、ライブラリに存在する
<シンボル 1> の弱い大域定義は使用されません。プログラムが直接 <シンボル 2> を呼び出すと、ライブラリにある定義が使用されます。<シンボル 2> の定義が重複して行われるとエラーになります。
#defineを使った引数リストの変更C コンパイラは、次の書式を使用した
#defineプリプロセッサ指令を受け入れます。
#define <識別子> (...) <代わりの指定>#define <識別子> (<識別子のリスト>, ...) <代わりの指定>マクロ定義の <識別子のリスト> が省略記号 (...) で終わる場合、呼び出し時にはマクロ定義内のパラメータ (省略記号を除く) 以外にも引数が存在することを意味します。省略記号がない場合、マクロ定義内のパラメータ (前処理トークンを含まない引数など) の数は、引数の数に一致します。引数内で省略記号表記を使用する
#defineプリプロセッサ指令の <代わりの指定> には、識別子__VA_ARGS__を使用してください。次に、引数リストを使用したマクロ機能の例を示します。
fprintf(stderr, "Flag");fprintf(stderr, "X = %d\n", x);puts("The first, second, and third items.");((x>y)?puts("x>y"):printf("x is %d but y is %d", x, y));事前定義済みのデータ
コンパイラは、機能定義ごとに
__func__という名前の静的配列、定数配列、および文字配列をあらかじめ定義します。関数の名前によって初期化されるこの配列は、静的な関数スコープ配列が使用できる場所であればどこでも使用できます。この配列は、たとえば自分自身を含んでいる関数の名前を出力するために使用できます。
#include <stdio.h>void the_func(void){printf("%s\n", __func__);/* ... */}この例では、この関数が呼び出されるたびに標準出力に
the_funcと出力されます。事前定義済みの名前
下の表に示す識別子は、オブジェクトに似たマクロとして事前に定義されています。
表 3-2 事前定義済みの識別子 __STDC____STDC__ 1 -Xc
__STDC__ 0 -Xa、-Xt
未定義 -Xs
__STDC__が未定義の場合 (#undef __STDC__)、コンパイラは警告を出します。-Xsモードでは__STDC__は定義されません。事前定義されているものは次のとおりです (
-Xcモードでは無効)。
__sun__unix__SUNPRO_C=0x510__`uname -s`_`uname -r` (例: __SunOS_5_7)__sparc(SPARC)__i386(x86)__BUILTIN_VA_ARG_INCR__SVR4__sparcv9(-Xarch=v9、v9a)
|
サン・マイクロシステムズ株式会社 Copyright information. All rights reserved. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |