ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris Studio 12.3: C ユーザーガイド Oracle Solaris Studio 12.3 Information Library (日本語) |
この付録では、サポートされている機能のうちの一部を詳細に説明し、使用例を示します。
5.2.4.2.2 項 浮動小数点型 <float.h> の特性
6.2.5 項 _Bool
6.2.5 項 _Complex 型
6.3.2.1 項 左辺値に限定されないポインタへの配列の変換
6.4.1 項 キーワード
6.4.2.2 項 定義済み識別子
6.4.3 項 汎用文字名
6.4.4.2 項 16 進数浮動小数点リテラル
6.4.9 項 コメント
6.5.2.2 項 関数呼び出し
6.5.2.5 項 複合リテラル
6.7.2 項 型指示子
6.7.2.1 項 構造体および共用体の指示子
6.7.3 項 型修飾子
6.7.4 項 関数指示子
6.7.5.2 項 配列宣言子
6.7.8 項 初期化
6.8.2 項 複合文
6.8.5 項 繰り返し文
6.10.3 項 マクロ置換
6.10.6 項 STDC プラグマ
6.10.8 項 __STDC_IEC_559 マクロおよび __STDC_IEC_559_COMPLEX マクロ
6.10.9 項 プラグマ演算子
機能: 5.2.4.2.2 浮動小数点型 <float.h> の特性
浮動小数点オペランドを持つ演算の値、および通常の算術変換および浮動小数点定数両方の影響を受ける値は、その型が必要とするより大きい可能性がある範囲および精度を持つ形式で評価されます。評価形式使用するは、次の表に示す FLT_EVAL_METHOD の実装定義値によって特徴付けられます。
表 D-1 FLT_EVAL_METHOD の値
|
SPARC アーキテクチャーで float.h をインクルードすると、FLT_EVAL_METHOD はデフォルトで 0 に展開され、すべての浮動小数点式はその型に従って評価されます。
x86 アーキテクチャーで float.h をインクルードすると、(-xarch=sse2 または -xarch=amd64 のときを除き) FLT_EVAL_METHOD はデフォルトで -1 に展開されます。すべての浮動小数点定数式はそれらの型に従って評価され、その他すべての浮動小数点式は long double として評価されます。
-flteval=2 を指定して、float.h をインクルードすると、FLT_EVAL_METHOD は 2 に展開され、すべての浮動小数点式は long double として評価されます。詳細については、「B.2.21 -flteval[={ any|2}]」を参照してください。
-xarch=sse2 (または sse3、ssse3、sse4_1、sse4_2 など、SSE2 プロセッサファミリのそれ以降のバージョン) または -m64 を x86 で指定して、float.h をインクルードすると、FLT_EVAL_METHOD は 0 に展開されます。すべての浮動小数点式はその型に従って評価されます。
浮動小数点式が double として評価されても、-Xt オプションがFLT_EVAL_METHOD の展開内容に影響することはありません。詳細については、「B.2.71 -X[c| a|t|s]」を参照してください。
-fsingle オプションを指定すると、浮動小数点式が単精度で評価されます。詳細については、「B.2.30 -fsingle」を参照してください。
-xarch=sse2 (または sse3、ssse3、sse4_1、sse4_2 など、SSE2 プロセッサファミリのそれ以降のバージョン) または -m64 を使用して x86 アーキテクチャーで -fprecision を指定して、float.h をインクルードすると、FLT_EVAL_METHOD が -1 に展開されます。
C99 の規格では、次のキーワードが追加されました。-xc99=none を指定した場合に、これらのキーワードを識別子として使用すると、コンパイラは警告を生成します。-xc99=none を指定していない場合は、これらのキーワードが識別子として使用されているときに、コンパイラがコンテキストに応じて警告またはエラーメッセージを生成します。
inline
_Imaginary
_Complex
_Bool
restrict
restrict で修飾されたポインタを使用してオブジェクトにアクセスするには、その オブジェクトへのすべてのアクセスで、直接的または間接的にそのポインタの値を使用する必要があります。ほかの方法によってそのオブジェクトにアクセスすると、定義されていない動作になる可能性があります。restrict 修飾子の意図される使用は、コンパイラが最適化を拡張する想定を行うことを許可することです。
restrict 修飾子を効果的に使用する方法についての例および説明は、「3.7.2 制限付きポインタ」を参照してください。
コンパイラで、定義済み識別子 __func__ がサポートされました。__func__ は、__func__ のあるコードでの現在の関数の名前を格納する文字配列として定義されています。
機能: 6.4.3 汎用文字名
UCN では、C のソースで英字ばかりでなく、任意の文字を使用することができます。UCN は次の書式を持ちます。
\u4 桁の 16 進値
\U8 桁の 16 進値
UCN では、0024 ($)、0040 (@)、0060 (?) を除く 00A0 未満の値や、D800 ~ DFFF の範囲内の値を指定してはいけません。
識別子や文字定数、文字列リテラルで UCN を使用して、C の基本文字セットにはない文字を表すことができます。
UCN の \Unnnnnnnn は、8 桁の短い識別子が nnnnnnnn である文字を表します (ISO/IEC 10646 で規定)。同様に、汎用文字名 \unnnn は、4 桁の短い識別子が nnnn (8 桁の短い識別子は 0000nnnn) である文字を表します。
文字 // は、文字定数、文字列リテラル、またはコメント内で // があるときを除いて、次の復帰改行まで (ただし、復帰改行は含まない) のすべての複数バイト文字を含むコメントを導入します。
機能: 6.5.2.2 関数呼び出し
暗黙の宣言は、1990 C 規格の場合とは異なり、1999 C 規格では許可されなくなりました。以前のバージョンの C コンパイラでは、-v (冗長形式) を指定した場合にだけ、暗黙の定義についての警告メッセージが生成されていました。暗黙の定義についてのこれらのメッセージおよび新しい追加警告は、識別子が暗黙に int または関数として宣言されているときは常に発行されるようになりました。
多数の警告メッセージが生成されることがあるため、この変更はたいていの場合はすぐにわかります。よくある原因には、<stdio.h> がインクルードされる必要がある printf など、使用される関数を宣言する適切なシステムヘッダーファイルのインクルードの失敗が含まれます。暗黙の宣言を受け入れる 1990 C 規格の動作は、-xc99=none を使ってサイレントに復元できます。
次の例に示すように、C コンパイラは、暗黙の関数宣言に対する警告を生成するようになりました。
example% cat test.c void main() { printf("Hello, world!\n"); } example% cc test.c "test.c", line 3: warning: implicit function declaration: printf example%
少なくとも 1 つの型指示子を、各宣言の宣言指示子で指定します。詳細は、「D.1.6 暗黙の int および暗黙の関数宣言の禁止」を参照してください。
暗黙の int 宣言時に、C コンパイラは次の例に示すように警告を発行するようになりました。
example% more test.c volatile i; const foo() { return i; } example% cc test.c "test.c", line 1: warning: no explicit type given "test.c", line 3: warning: no explicit type given example%
この機能は「struct hack」とも呼ばれます。構造体の最後のメンバーを、int foo[]; などのゼロ長の配列にすることを許可します。この種類の構造体は、malloc で割り当てられたメモリーにアクセスするためのヘッダーとして一般的に使用されます。
たとえば、struct s { int n; double d[]; } S; では、配列 d が不完全な配列型です。C コンパイラは、この S のメンバーのメモリーオフセットをカウントしません。つまり、sizeof(struct s) は S.n のオフセットと同一になります。
たとえば S.d[10] = 0; のように、d は通常の配列メンバーと同様に使用できます。
C コンパイラが不完全な配列型をサポートしていない場合は、次の例の DynamicDouble のような構造体を定義および宣言します。
typedef struct { int n; double d[1]; ) DynamicDouble;
ここで、配列 d は不完全な配列型ではなく、1 つのメンバーを指定して宣言されています。
次に、ポインタdd を宣言してメモリーを割り当てます。
DynamicDouble *dd = malloc(sizeof(DynamicDouble)+(actual_size-1)*sizeof(double));
そのあとで、次のように S.n にオフセットのサイズを格納します。
dd->n = actual_size;
コンパイラが不完全な配列型をサポートしているため、1 つのメンバーを指定して配列を宣言することなく、同一の結果を得ることができます。
typedef struct { int n; double d[]; } DynamicDouble;
ここで、ポインタ dd を宣言し、以前と同様にメモリーを割り当てます。ただし、actual_size から 1 を引く必要はなくなりました。
DynamicDouble *dd = malloc (sizeof(DynamicDouble) + (actual_size)*sizeof(double));
以前と同様に、オフセットは S.n に保存されます。
dd->n = actual_size;
同一の指示子と修飾子のリストで、直接または typedef により同一の修飾子が複数ある場合は、動作は型修飾子が 1 つだけの場合と同様になります。
C90 では、次のコードではエラーが発生します。
%example cat test.c const const int a; int main(void) { return(0); } %example cc -xc99=none test.c "test.c", line 1: invalid type combination
ただし、C99 では、C コンパイラで複数の修飾子を使用できます。
%example cc -xc99 test.c %example
1999 C ISO 標準で定義されているインライン関数は、完全にサポートされています。
C 標準によると、インラインは C コンパイラに対する推奨に過ぎません。C コンパイラは、何もインライン化しないこと、または実際の関数への呼び出しをコンパイルすることを選択できます。
Oracle Solaris Studio C コンパイラは、最適化レベル -xO3 以上でコンパイルし、さらにオプティマイザのヒューリスティックがそうすることの利点があると判断した場合を除いて、C 関数呼び出しをインライン化しません。C コンパイラは、関数のインライン化を強制する方法を用意していません。
static インライン関数は単純です。インライン関数指定子によって定義された関数を、参照時にインライン化するか、実際の関数を呼び出すかのどちらかです。コンパイラは、参照ごとにどちらを実行するかを選択できます。コンパイラは、-xO3 以上でインライン化に利点があるかどうかを判定します。インライン化の利点がない場合 (または -xO3 未満の最適化の場合)、実際の関数への参照がコンパイルされ、関数定義がオブジェクトコードにコンパイルされます。プログラムが関数のアドレスを使用している場合は、実際の関数がコンパイルされてオブジェクトコードの一部になり、インライン化されないことに注意してください。
extern インライン関数はより複雑です。extern インライン関数には、インライン定義および extern インライン関数の 2 種類があります。
インライン定義とは、キーワード inline を使って、static または extern キーワードなしで定義された関数です。ソース (またはインクルードファイル) 内に出現するすべてのプロトタイプでも、キーワード inline が含まれ、static または extern キーワードはなしです。インライン定義に対して、コンパイラは関数のグローバル定義を作成してはいけません。このことは、インライン化されないインライン定義へのあらゆる参照は、どこか他の場所で定義されたグローバル関数への参照になることを意味します。言い換えると、この変換ユニット (ソースファイル) をコンパイルすることで作成されたオブジェクトファイルには、インライン定義に対応するグローバルシンボルは含まれません。インライン化されない関数へのあらゆる参照は、リンク時にほかのオブジェクトファイルまたはライブラリから提供される extern (大域) シンボルになります。
extern インライン関数は、extern 記憶クラス指定子 (つまり、関数定義またはプロトタイプ) を持つファイルスコープ宣言によって宣言されます。extern インライン関数の場合、コンパイラはその関数に対応するグローバル定義を、結果として生成されるオブジェクトファイルの中で提供します。コンパイラは、関数定義の提供元である変換ユニット (ソースファイル) の中で観察された関数へのすべての参照をインライン化するか、グローバル関数を呼び出すかを選択できます。
関数呼び出しが実際にインライン化されるかどうかに依存する任意のプログラムの動作は、未定義です。
外部リンケージを持つインライン関数が、変換ユニットのどこであっても静的変数を宣言または参照してはいけないことにも注意してください。
ほとんどのプログラムのために Oracle Solaris Studio C コンパイラから、GNU C コンパイラの extern インライン関数の実装と互換性のある動作を取得するには、-features=no%extinl フラグを使用します。このフラグが指定されているときは、Oracle Solaris Studio C コンパイラはあたかも static インライン関数と宣言されたかのように、関数を処理します。
この手法が互換性を維持できない状況の 1 つは、関数のアドレスを取る場合です。gcc ではこのアドレスはグローバル関数のアドレスになりますが、Sun のコンパイラではローカルの static 定義アドレスが使用されます。
関数宣言子内のパラメータの配列宣言子で static キーワードを使用することが可能になりました。この場合は、コンパイラが、宣言する関数に多数の要素が引き渡されることを少なくとも認識することができます。これにより、オプティマイザで従来は不可能だった想定が可能になります。
C コンパイラは、配列パラメータをポインタに変換します。したがって、void foo(int a[]) は void foo(int *a) と同義になります。
void foo(int * restrict a); などの型修飾子を指定すると、C コンパイラはそれを配列文 void foo(int a[restrict]); で表現します。これは、実質的には制限付きポインタを宣言するのと同義です。
C コンパイラは、配列サイズに関する情報を保持するためにも static 修飾子を使用します。たとえば、void foo(int a[10]) を指定した場合でも、コンパイラはこれを void foo(int *a) と表現します。ポインタが NULL ではなく、少なくとも 10 個の要素を持つ整数配列へのポインタであることをコンパイラに認識させるには、void foo(int a[static 10]) のように static 修飾子を使用します。
VLA は、alloca 関数を呼び出した場合と同様に、スタックに割り当てられます。VLA の有効期間は、有効範囲に関係なく、alloca の呼び出しによりスタックに割り当てられたデータと同様に、関数から復帰するまでです。割り当てられた領域は、VLA が割り当てられた関数から復帰してスタックが解放されるときに同時に解放されます。
可変長配列では、一部の制約がまだ有効になっていません。制約に違反すると、定義されていない結果になります。
#include <stdio.h> void foo(int); int main(void) { foo(4); return(0); } void foo (int n) { int i; int a[n]; for (i = 0; i < n; i++) a[i] = n-i; for (i = n-1; i >= 0; i--) printf("a[%d] = %d\n", i, a[i]); } example% cc test.c example% a.out a[3] = 1 a[2] = 2 a[1] = 3 a[0] = 4
機能: 6.7.8 初期化
指示付き初期化子は、数値プログラミングで一般的なスパース配列を初期化する仕組みです。
指示付き初期化子によって、システムプログラミングで一般的なスパース構造体を初期化したり、先頭メンバーであるかどうかに関係なく、任意のメンバーを使って共用体を初期化したりできます。
例を挙げて考えてみます。最初の例は、指示付き初期化子を使って配列を初期化する方法を示しています。
enum { first, second, third }; const char *nm[] = { [third] = "third member", [first] = "first member", [second] = "second member", };
次の例は、指示付き初期化子を使用して struct オブジェクトのフィールドを初期化する方法を示しています。
division_t result = { .quot = 2, .rem = -1 };
次の例は、指示付き初期化子を使用して、これ以外の方法では誤解を生むおそれがある複雑な構造体を初期化する方法を示しています。
struct { int z[3], count; } w[] = { [0].z = {1}, [1].z[0] = 2 };
1 つの指示子で両端から配列を作成することができます。
int z[MAX] = {1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0};
MAX が 10 より大きい場合、この配列の中央には値ゼロの要素が含まれます。MAX が 10 より小さい場合、最初の 5 つの初期化子が提供する値の一部は、2 つ目の 5 つの値によって置き換えられます。
共用体のすべてのメンバーを初期化することができます。
union { int i; float f;} data = { .f = 3.2 };
C コンパイラは、次の例のように型宣言と実行可能コードの混在を受け入れます。
#include <stdio.h> int main(void){ int num1 = 3; printf("%d\n", num1); int num2 = 10; printf("%d\n", num2); return(0); }
C コンパイラは、for ループ文の最初の式として型宣言を受け入れます。
for (int i=0; i<10; i++){ //loop body };
for ループの初期化文で宣言した変数の有効範囲は、ループ全体になります (制御式と繰り返し式を含む)。
機能: 6.10.3 マクロ置換
C コンパイラで、次の形式の #define プリプロセッサ指令を使用することができます。
#define identifier (...) replacement_list #define identifier (identifier_list, ...) replacement_list
マクロ定義で identifier_list が省略符号で終わる場合は、マクロ定義でのパラメータよりも呼び出しの引数の方が多いことを示します (省略符号を除く)。それ以外の場合は、マクロ定義でのパラメータ数 (プリプロセッサトークンを含まないそれらの引数を含む) が引数の個数と一致します。引数に省略符号表記を使用する #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));
機能: 6.10.9 プラグマ演算子
_Pragma ( string-literal ) という形式の単項演算子の式は、次のように処理されます。
文字列定数の L 接頭辞がある場合は削除されます。
前および後ろの二重引用符は削除されます。
エスケープシーケンス ' は、二重引用符に置換されます。
エスケープシーケンス \\ は、1 つの \ に置換されます。
生成されたプリプロセッサトークンのシーケンスは、プラグマの指令でのプリプロセッサトークンと同様に処理されます。
単項演算子の式にある元の 4 つのプリプロセッサトークンは削除されます。
_Pragma は、#pragma と比較して、マクロ定義で使用可能であるという利点があります。
_Pragma("string") は、#pragma string と正確に同じ動作をします。次の例を考えてみましょう。まず、例のソースコードがリストされます。次に、プリプロセッサ処理後の例のソースがリストされます。
example% cat test.c #include <omp.h> #include <stdio.h> #define Pragma(x) _Pragma(#x) #define OMP(directive) Pragma(omp directive) void main() { omp_set_dynamic(0); omp_set_num_threads(2); OMP(parallel) { printf("Hello!\n"); } } example% cc test.c -P -xopenmp -xO3example% cat test.i
以下は、プリプロセッサ終了後のソースを示しています。
void main() { omp_set_dynamic(0); omp_set_num_threads(2); # pragma omp parallel { printf("Hellow!\n"); } } example% cc test.c -xopenmp example% ./a.out Hello! Hello! example%