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 秒、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
を除くすべてのコンパイラモードで予約されています。予約語_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 ビットの情報を格納できるのに対し、long
long
には 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
またはU
unsigned
l
またはL
long
ll
またはLL
long long
1lu
、LU
、Lu
、lU
、ul
、uL
、Ul
、UL
のいずれかunsigned long
llu
、LLU
、LLu
、llU
、ull
、ULL
、uLL
、Ull
のいずれかunsigned long long
2
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-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
<整数>(
<変数>[,
<変数>])
- 整列プラグマで指定した <変数> のメモリーはデフォルト値によらず、すべて <整数> バイト境界に揃えられます。
- <整数> には 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 オプションを使用すると、このプラグマは無効になり、すべての警告メッセージが抑止されます。
#pragma
error_messages
(on
, <タグ>... <タグ>)
#pragma
error_messages
(off
, <タグ>... <タグ>)
- off オプションは、C コンパイラまたは lint プログラムが指定トークンから始まる特定のメッセージを発行することを禁止します。この特定のエラーメッセージに対するプラグマの指定は、別の
#pragma error_messages
によって無効にされるか、コンパイルが終了するまで有効です。#pragma
error_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(
関数[,
関数])
指定したルーチン名のインライン化を制御します。このプラグマはファイル全体に対して有効です。このプラグマでは、大域的なインライン化のみ制御可能であり、呼び出し元固有の制御を行うことはできません。
#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)
を指定すると、int
、long
、long
long
、float
、double
、long
double
、pointer
が、それぞれの自然境界ではなく、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
を使用すると、その関数が使用されるときは、必ずその関数の新しい定義でリンクされるようになります。
#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. |
ホーム | 目次 | 前ページへ | 次ページへ | 索引 |