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 が優先されます。

Bourne シェルの場合は以下のように入力します。

$ TMPDIR=<ディレクトリ>; export TMPDIR

C シェルの場合は以下のように入力します。

% setenv TMPDIR <ディレクトリ>

SUNPRO_SB_INIT_FILE_NAME

.sbinit(5)ファイルが格納されるディレクトリの絶対パス名。この変数は、-xsb または -xsbfast フラグが使用されている場合にのみ使用されます。

PARALLEL

(SPARC) プログラムをマルチプロセッサ上で実行する場合に、使用するプロセッサの数を指定します。対象マシンに複数のプロセッサが搭載されている場合、スレッドは個々のプロセッサにマップできます。プログラムを実行すると、指定したプロセッサ数のスレッドが生成され、並列化されたプログラムの各部が実行されます。

SUNW_MP_THR_IDLE

並列ジョブの分担部分を終えた後の各スレッドの状態を制御します。SUNW_MP_THR_IDLE は、spin または sleep [n s|n ms] に設定できます。デフォルトは spin であり、これはスレッドがスピン待ちに入ることを意味します。もう一方の選択肢、sleep [n s|n ms] は、n に指定された時間だけスレッドをスピン待ち状態にし、その後休眠させることを意味します。待ち時間の単位は秒 (s: デフォルトの単位) かミリ秒 (ms) で、1s は 1 秒、10 ms は 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 を除くすべてのコンパイラモードで予約されています。予約語 _asmasm の同義語で、すべてのコンパイラモードの下で使用できますが、-Xc モードで使用した場合は警告が出されます。asm 文の形式は次のとおりです。

asm("<文字列>"):


ここで、<文字列> は有効なアセンブリ言語文です。asm 文は関数の本体内部に指定しなければなりません。

_Restrict

コンパイラが効果的にループの並列実行を行うには、ある種の左辺値 (lvalue) が異なる記憶領域を指しているかどうかを判別する必要があります。別名とは、記憶領域が異なっていない左辺値のことです。オブジェクトを示す 2 つのポインタが別名かどうかを判別するのは、プログラム全体の解析を必要とすることがあるため、難しく時間のかかる作業です。

次の関数 vsq() を例にとって考えてみましょう。

void vsq(int n, double * a, double * b)
{
    int i;
    for (i=0; i<n; i++) b[i] = a[i] * a[i];
}


コンパイラは、ポインタ ab が異なるオブジェクトにアクセスすることがわかっていれば 、ループの異なる繰り返しの実行を並列化することができます。ポインタ ab でアクセスされるオブジェクトが重なりあっている場合には、コンパイラが並列でループを実行するのは安全ではありません。コンパイル時には、コンパイラは関数 vsq() を単純に解析しただけでは ab でアクセスするオブジェクトが重なり合っているかどうかがわかりません。この情報を得るにはプログラム全体の解析が必要です。

コンパイラがポインタ別名解析を実行できるよう、制限付きポインタを使用して異なるオ ブジェクトを示すポインタを指定します。制限付きポインタをサポートするために、キーワード _Restrict が Sun ANSI/ISO C コンパイラによって拡張として認識されます。次に vsq() の関数引数を制限付きポインタとして宣言する例を示します。

void vsq(int n, double * _Restrict a, double * _Restrict b)


ポインタ ab が制限付きポインタとして宣言されると、コンパイラは ab が示す記憶域の領域が異なるものだと認識します。この別名情報により、コンパイラはループを並列化することができます。

_Restrict キーワードは volatile と同じような型修飾子で、ポインタ型だけを修飾します。_Restrict は、コンパイルモード -Xa (デフォルト) と -Xt でのみキーワードとして認識されます。これらの 2 つのモードでは、コンパイラはマクロ __RESTRICT を定義して、ユーザーが制限付きポインタを用いて移植可能なコードを書けるようにします。

コンパイラはマクロ __RESTRICT を定義して、ユーザーが制限付きポインタを用いて移植可能なコードを書けるようにします。たとえば、次のコードは Sun ANSI/ISO C コンパイラのすべてのコンパイルモードで使用でき、制限付きポインタをサポートしていない他のコンパイラでも使用できます。

#ifdef __RESTRICT
#define restrict _Restrict
#else
#define restrict
#endif

 
void 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() の例で示したポインタ ab をキーワード _Restrict で修飾します。

_Restrict は正しく使用することが重要です。制限付きポインタとして修飾されているポインタが、同一オブジェクトを指していると、ループは誤って並列化されることがあり、未定義の動作が発生します。たとえば、関数 vsq() のポインタ ab が重なり合ったオブジェクトを指し、b[i]a[i+1] は同じオブジェクトであるとします。ab が制限付きポインタとして宣言されなければ、ループは順次に実行されます。ab が誤って制限付きポインタとして宣言されると、コンパイラはループの実行を並列化することがあります。b[i+1] の計算は、b[i] の計算後に実行される必要が生じるため、これは安全とは言えません。

long long データ型

Sun ANSI/ISO C コンパイラにはデータ型 long long および unsigned long long があり、これらはデータ型 long と類似しています。long には 32 ビットの情報を格納できるのに対し、long long には 64 ビットの情報を格納できます。long long-Xc モードでは使用できません。

long long データ型の入出力

long long データ型を出力または入力するには、変換指定子の前に "ll" の接頭辞を付けてください。たとえば、long long データ型をもつ変数 llvar を符号付き 10 進形式で出力するには、次のように指定します。

printf("%lld\n", llvar);


通常の算術変換

2 項演算子によっては、両方のオペランドの型を共通の型にするために変換することがあります。この時、結果の型も共通の型となります。この変換は通常の算術変換と呼ばれます。

定数

ここでは、Sun ANSI/ISO C コンパイラに固有の定数に関する情報について説明します。

整数定数

下表に示すように、10 進数、8 進数、16 進数の定数に接尾辞を付けて型を示すことができます。

表 3-1   データ型の接尾辞
接尾辞
u または U unsigned
l または L long
ll または LL long long1
luLULulUuluLUlUL のいずれか unsigned long
lluLLULLullUullULLuLLUll のいずれか
unsigned long long2
1 long long は -Xc モードでは使用できません。

2 unsigned long long は -Xc モードでは使用できません。


コンパイラが接尾辞を持たない定数の型を割り当てる場合、定数の大きさに応じて、次のリストから値が表現できる最初の型を使用します。

文字定数

エスケープシーケンスではない複数の文字からなる文字定数は、各文字が持つ数値から派生する値を持ちます。たとえば定数 '123' の持つ値は以下のようになります。

0 '3' '2' '1'


あるいは 0x333231 です。

-Xs オプション使用の場合、あるいは ANSI/ISO でない他の C では、この値は以下のようになります。

0 '1' '2' '3'

あるいは 0x313233 です。

インクルードファイル

C コンパイルシステムに含まれる標準ヘッダーファイルをインクルードするには、次の書式を使用します。

#include <stdio.h> 


山括弧(<>)を使用するとプリプロセッサはシステム内の標準の場所、通常は
/usr/include ディレクトリにあるヘッダーファイルを検索します。

ユーザーが自分のディレクトリに格納したヘッダーファイルの場合は、次のように書式が異なります。

#include "header.h"


二重引用符 ("") を使用すると、プリプロセッサはまず、この #include 行を含むファイルが格納されているディレクトリの中で header.h を検索します。

ヘッダーファイルがインクルードされたソースファイルと同じディレクトリにない場合は、cc コマンドで -I オプションを使用して、ヘッダーファイルが格納されているディレクトリのパスを指定してください。たとえば次のように、ソースファイル mycode.c の中で stdio.hheader.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 回通ると x1 になり、2 回目で 0.1、3 回目で 0.01 と続き、やがてはマシンによって値を表現できる許容範囲の下限に到達します。

 次にループを実行すると、表現可能な最小の数は次のようになると考えられます。

 1.234567e-38

次にループを実行すると、仮数部から「盗んだ」ものを指数部に「与える」ことによって数値が修正され、新しい値は次のようになります。

 1.23456e-39

その次はさらに、

 1.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 <整数> (<変数>[,<変数>])

整列プラグマで指定した <変数> のメモリーはデフォルト値によらず、すべて <整数> バイト境界に揃えられます。

#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 オプションを使用すると、このプラグマは無効になり、すべての警告メッセージが抑止されます。

#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(関数[,関数])

指定したルーチン名のインライン化を制御します。このプラグマはファイル全体に対して有効です。このプラグマでは、大域的なインライン化のみ制御可能であり、呼び出し元固有の制御を行うことはできません。

#pragma inline は、現在のファイル内にある、指定したルーチンに一致する呼び出しをインライン化するようコンパイラに指示します。この指示は、無視されることがあります。たとえば、関数本体が別のモジュールに存在していて、-xcrossfile オプションが使用されていない場合などです。

#pragma no_inline は、現在のファイル内にある、指定したルーチンに一致する呼び出しをインライン化しないようコンパイラに指示します。

次の例に示すように、#pragma inline および #pragma no_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 以上の最適化に利用します。

#pragma opt level (<関数>[,<関数>])

指定した関数サブプログラムに対する最適化レベルを指定します。最適化レベルとしては 0〜5 を選択することができます。0 に設定すると、最適化は無効になります。このプラグマを使用する前に、関数サブプログラムのプロトタイプを作成しておく必要があります。

プラグマ内に指定される関数の最適化レベルは、-xmaxopt の値に下げられます。-xmaxopt=off の場合、プラグマは無視されます。

#pragma pack(n)

構造体のメンバーの境界整列を制御します。デフォルトでは、構造体のメンバーは、char 型なら 1 バイト、short 型なら 2 バイト、整数なら 4 バイトというように、その自然境界で整列させられます。n を指定する場合には、すべての構造体メンバーに対して最も厳密な自然境界整列となる 2 の乗数にします。ゼロは受け入れられません。
このプラグマを使用すると、構造体メンバーの境界整列を指定できます。たとえば、#pragma pack(2) を指定すると、intlonglong longfloatdoublelong doublepointer が、それぞれの自然境界ではなく、2 バイト境界に整列されます。

n がプラットフォームで最も厳密な整列を指示する値 (Intel では 4、SPARC v8 では 8、SPARC v9 では 16) か、それより大きな値の場合は、自然境界整列が有効になります。n が省略された場合も、メンバーは自然境界整列に戻ります。

#pragma pack(n) 指令は、その指令から次の #pragma pack 指令の間のすべての構造体の定義に適用されます。別の翻訳単位で同じ構造体に対して異なる #pragma pack の定義が行われている場合、プログラムは予期しない形でコンパイルに失敗することがあります。特に、#pragma pack(n) は、事前にコンパイルされたライブラリのインタフェースを定義するヘッダーをインクルードする前には使用しないでください。
#pragma pack(n) は、プログラムコード内の境界整列を変更するすべての構造体の直前に挿入することをお勧めします。そして、その構造体の直後に #pragma pack() を続けてください。


注 - #pragma pack を使用して構造体メンバーを自然境界以外の境界で整列させると、これらのフィールドへのアクセスが発生した場合に 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 を使用すると、その関数が使用されるときは、必ずその関数の新しい定義でリンクされるようになります。

#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_extnmae 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(<関数>[,<関数>])

指定した関数の戻り値が呼び出し元のどのメモリーとも別名処理されないことを表明します。つまり、この呼び出しでは、新しいメモリー位置が返されます。この表明により、オプティマイザはポインタ値を追跡して、メモリー位置を明確にし、ループのスケジューリングやパイプライン処理、並列化処理を改善することができます。表明が偽の場合には、プログラムの動作は未定義になります。

次の例に示すように、このプラグマは、指定した関数のプロトタイプを宣言した後でのみ使用できます。

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 <シンボル>

これは <シンボル> を弱いシンボルとして定義しています。<シンボル> の定義が見つからなくても、リンカーはメッセージ等を出さなくなります。

#pragma weak <シンボル 1> = <シンボル 2>

これは <シンボル 1> を、<シンボル 2> の別名の弱いシンボルと定義します。この形式のプラグマは、ソースファイルまたはそこにインクルードされたヘッダーファイルのいずれかで、<シンボル 2> を定義した同じ変換ユニットの中に限り使用できます。それ以外で使用された場合は、コンパイルエラーになります。
プログラムが <シンボル 1> を呼び出しますが、それがプログラム中で定義されていない場合には、<シンボル 1> がリンクライブラリ中の弱いシンボルになっていると、リンカーはライブラリにある定義を使用します。しかし、プログラム自身が <シンボル 1> を定義してある場合、プログラムでの定義が優先され、ライブラリに存在する
<シンボル 1> の弱い大域定義は使用されません。プログラムが直接 <シンボル 2> を呼び出すと、ライブラリにある定義が使用されます。<シンボル 2> の定義が重複して行われるとエラーになります。

#define を使った引数リストの変更

C コンパイラは、次の書式を使用した #define プリプロセッサ指令を受け入れます。

#define <識別子> (...) <代わりの指定>
#define <識別子> (<識別子のリスト>, ...) <代わりの指定>

マクロ定義の <識別子のリスト> が省略記号 (...) で終わる場合、呼び出し時にはマクロ定義内のパラメータ (省略記号を除く) 以外にも引数が存在することを意味します。省略記号がない場合、マクロ定義内のパラメータ (前処理トークンを含まない引数など) の数は、引数の数に一致します。引数内で省略記号表記を使用する #define プリプロセッサ指令の <代わりの指定> には、識別子 __VA_ARGS__ を使用してください。次に、引数リストを使用したマクロ機能の例を示します。

#define debug(...) fprintf(stderr, __VA_ARGS__)
#define showlist(...) puts(#__VA_ARGS__)
#define report(test, ...) ((test)?puts(#test):\
               printf(__VA_ARGS__))
debug("Flag");
debug("X = %d\n",x);
showlist(The first, second, and third items.);
report(x>y, "x is %d but y is %d", x, y);

結果は次のようになります。

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 モードでは無効)。

次の事前定義されているものは、あらゆるモードで有効です。

コンパイラにより、次のオブジェクト形式のマクロが事前定義されます。

__PRAGMA_REDEFINE_EXTNAME

これにより、プラグマが認識されます。

__RESTRICT は、-Xa および -Xt モードでのみ有効です。


サン・マイクロシステムズ株式会社
Copyright information. All rights reserved.
ホーム   |   目次   |   前ページへ   |   次ページへ   |   索引