[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]  


C 言語ファミリに対する拡張

GNU C は、ANSI の標準 C にはない特徴的な言語機能をいくつか提供しています (`-pedantic' オプションは、 これらの機能が使われた場合に警告メッセージを出力するよう GNU CC に対して指示するものです)。 条件コンパイルにおいて、これらの機能が利用可能であるかどうかをテストするためには、 __GNUC__ というマクロが事前に定義されているかどうかをチェックします。 この__GNUC__ というマクロは、 GNU CC では常に定義されています。

これらの拡張機能は C と Objective C で利用可能です。 また、 そのほとんどは C++ でも利用可能です。 C++ にのみ適用可能な拡張機能については、 See section C++ 言語に対する拡張

式の中の文と宣言

GNU C においては、 丸括弧 () で囲まれた複文を1つの式とみなすことができます。 これによって、 1つの式の中でループ、 switch 文、 ローカル変数を使うことができます。

複文とは波括弧 {} で囲まれた一連の文であるということを思い出してください。 複文を1つの式とみなす場合、 丸括弧 () が波括弧 {} のまわりを囲むことになります。 例えば、

({ int y = foo (); int z;
   if (y > 0) z = y;
   else z = - y;
   z; })

は、 foo () の戻り値の絶対値を表す式としては(やや必要以上に複雑ではありますが)正当なものです。

複文の末尾には、 式の後ろにセミコロンが続いたものがなければなりません。 この部分式の値が、式全体の値となります (波括弧 {} で囲まれた部分の末尾に異なる種類の文を使うと、 式全体の型は void となり、 したがって事実上値を持たないことになります)。

この機能は、 マクロ定義を「安全」な (したがって、 個々のオペランドをただ1回のみ評価する) ものにするのに特に役立ちます。 例えば、 「最大値を返す」関数は、 標準 C では以下に示すようにマクロとして定義するのが一般的です。

#define max(a,b) ((a) > (b) ? (a) : (b))

しかし、 この定義は a もしくは b のいずれか一方を2回評価するので、 オペランドが副作用を持つ場合には望ましくない結果をもたらします。 オペランドの型が分かっている場合(ここでは int であると仮定しましょう)、 GNU C では、 このマクロを以下に示すように安全に定義することができます。

#define maxint(a,b) \
  ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

列挙定数の値、 ビット・フィールドのビット幅、 静的変数の初期値などの定数式に、 文を組み込むことはできません。

オペランドの型が分からない場合でも、 複文を1つの式に組み込むことは可能です。 しかしその場合には、 typeof (see section typeof による型への参照)、 もしくは、 型の名前付け (see section 式の型に対する名前付け)を使わなければなりません。

局所的に宣言されたラベル

個々の複文式(statement expression)の中で局所ラベルを宣言すると、 その複文式自体がその局所ラベルのスコープになります。 局所ラベルは単なる識別子です。 通常の goto 文によってそこへジャンプすることができますが、 このようなジャンプはその局所ラベルが属する複文式の中からのみ可能です。

局所ラベルの宣言は以下のように行います。

__label__ label;

もしくは

__label__ label1, label2, @dots{};

局所ラベルの宣言は、 複文式の先頭、 すなわち `({' の直後、 かつ、 通常の宣言のどれよりも前になければなりません。

ラベルの宣言はラベルの名前を定義するもので、 ラベル自体を定義する訳ではありません。 ラベルの定義は通常どおり label: によって、 複文式に含まれる複数の文の中のどこかで行わなければなりません。

局所ラベル機能が役に立つのは、 複文式がマクロの中でしばしば使われるからです。 あるマクロの中に入れ子になったループがあると、 そのループから抜け出るのに goto が役に立ちます。 しかし、 スコープが関数全体である通常のラベルを使うことはできません。 そのマクロが1つの関数の中で数回展開されることがあると、 同じ関数の中でそのラベルが複数回定義されることになるからです。 局所ラベルはこのような問題を回避してくれます。 以下にその例を示します。

#define SEARCH(array, target)                     \
({                                               \
  __label__ found;                                \
  typeof (target) _SEARCH_target = (target);      \
  typeof (*(array)) *_SEARCH_array = (array);     \
  int i, j;                                       \
  int value;                                      \
  for (i = 0; i < max; i++)                       \
    for (j = 0; j < max; j++)                     \
      if (_SEARCH_array[i][j] == _SEARCH_target)  \
        { value = i; goto found; }              \
  value = -1;                                     \
 found:                                           \
  value;                                          \
})

値としてのラベル

カレントな関数 (あるいは、 そのラベルを含んでいる関数) の中で定義されたラベルのアドレスを、 単項演算子 `&&' で獲得することができます。 この値の型は void * です。 この値は定数であり、 void * 型の定数が正当であるところではどこでも使うことができます。 以下に例を示します。

void *ptr;
...
ptr = &&foo;

この値を使うためには、 そのラベルにジャンプすることができる必要があります。 これは評価 goto(computed goto)文 (10) goto *exp; で行います。 以下に例を示します。

goto *ptr;

評価 goto 文では void * 型の任意の式を使うことができます。

このような定数の用途の1つに、 ジャンプ・テーブルとして機能する静的配列の初期化があります。

static void *array[] = { &&foo, &&bar, &&hack };

こうすると、 以下のようにインデックスを使ってラベルを選択することができます。

goto *array[i];

ここで、 配列の添字が上限内にあるかどうかチェックされないことに注意してください。 C における配列のインデックスでは、 このようなチェックは決して行われません。

このようなラベル値の配列は、 switch 文が目的とするところと同様の効果を持ちます。 switch 文のほうがよりすっきりしていますので、 実現しようとしていることが switch 文にうまく適合しない場合以外は、 ラベル値の配列ではなく、 switch 文を使うようにしてください。

ラベル値の別の用途として、 スレッド化されたコード用として、 インタープリタの内部で使うという方法があります。 非常に高速なディスパッチ処理を実現するために、 インタープリタ関数内のラベルの値をスレッド化されたコードの中に持たせることができます。

別の関数の中のコードにジャンプするために、 このメカニズムを使うことができます。 しかし、 このようなことを行うと、 まったく予想不可能な事態が発生するでしょう。 これを回避する最良の方法は、 ラベルのアドレスを自動変数にのみ格納して、 決して引数として渡さないということです。

入れ子関数

入れ子関数(nested function)は、 別の関数の内部に定義された関数です (入れ子関数は GNU C++ ではサポートされていません)。 入れ子関数の名前は、 それが定義されたブロックの内部に局所的なものです。 例えば以下では、 square という名前の入れ子関数を定義して、 それを2回呼び出しています。

foo (double a, double b)
{
  double square (double z) { return z * z; }

  return square (a) + square (b);
}

入れ子関数を包含している関数が持つ変数のうち、 入れ子関数が定義された箇所において可視なものすべてに対して、 入れ子関数の中からアクセスすることができます。 これを 構文スコーピング(lexical scoping)と呼びます。 ここでは例として、 offset という名前を持つ、 継承された変数を使う入れ子関数を示します。

bar (int *array, int offset, int size)
{
  int access (int *array, int index)
    { return array[index + offset]; }
  int i;
  ...
  for (i = 0; i < size; i++)
    ... access (array, i) ...
}

関数の中で変数定義ができるところであればどこでも、 入れ子関数の定義を行うことができます。 つまり、 任意のブロック内で、 そのブロックの最初の文の前であれば、 入れ子関数の定義が可能です。

入れ子関数のアドレスをどこかに格納したり別の関数へ渡したりすることによって、 入れ子関数の名前が有効なスコープの外部からでも、 入れ子関数を呼び出すことができます。

hack (int *array, int size)
{
  void store (int index, int value)
    { array[index] = value; }

  intermediate (store, size);
}

ここで、 関数 intermediatestore のアドレスを引数として受け取っています。 intermediatestore を呼び出すと、 store に渡された引数は array への値の格納に使われます。 しかし、 このテクニックは、 入れ子関数を包含している関数(この例では hack)が終了しない限りにおいてのみ有効です。

入れ子関数を包含する関数が終了した後に、 その入れ子関数のアドレスを使ってその入れ子関数を呼び出そうとすると、 とんでもない事態が発生することになるでしょう。 入れ子関数を包含するスコープが終了した後にその入れ子関数を呼び出してしまい、 その入れ子関数が既にスコープ内には存在しない変数を参照したとしても、 運良く問題に遭遇せずにすむこともあるかもしれませんが、 そのような危険を冒すのは賢明なことではありません。 しかし、 スコープ内には存在しなくなってしまったシンボルを入れ子関数が一切参照していないのであれば、 何も問題は発生しないはずです。

GNU CC では、 入れ子関数のアドレスを取る方法は、 トランポリン(trampoline)と呼ばれるテクニックを使って実装されています。

入れ子関数を包含する関数から継承されたラベルが、 その包含関数の中で明示的に宣言されているのであれば、 入れ子関数はそのラベルにジャンプすることができます (see section 局所的に宣言されたラベル)。 そのようなジャンプは、 goto を実行した入れ子関数だけでなく、 その入れ子関数が呼び出されるまでに途中で呼び出された関数も終了させて、 即時に包含関数側に制御を戻します。 以下に例を示します。

bar (int *array, int offset, int size)
{
  __label__ failure;
  int access (int *array, int index)
    {
      if (index > size)
        goto failure;
      return array[index + offset];
    }
  int i;
  ...
  for (i = 0; i < size; i++)
    ... access (array, i) ...
  ...
  return 0;

 /* access がエラーを検出すると、
    制御は access からここへ移る */
 failure:
  return -1;
}

入れ子関数は常に内部結合(internal linkage)を行います。 入れ子関数を extern を指定して宣言するのは誤りです。 入れ子関数を定義する前に、 その入れ子関数を宣言する必要がある場合には、 auto を使ってください (これ以外の場合では、 関数宣言に対して auto を使うのは無意味です)。

bar (int *array, int offset, int size)
{
  __label__ failure;
  auto int access (int *, int);
  ...
  int access (int *array, int index)
    {
      if (index > size)
        goto failure;
      return array[index + offset];
    }
  ...
}

関数呼び出しの組み立て

以下に説明する組み込み関数を使うことで、 ある関数の引数の数や型が分からなくても、 その関数が受け取った引数を記録して、 別の関数を同じ引数で呼び出すことができます。

また、 その別の関数が返そうとしたデータの型が分からなくても (そのデータ型が呼び出し側が期待しているものと同じである限り)、 その関数呼び出しの戻り値を記録して、 後にその値を返すことができます。

__builtin_apply_args ()
この組み込み関数は、 カレントな関数に対して渡された引数と同一の引数を使って関数呼び出しを行う方法を記述するデータへの void * 型のポインタを返します。 この関数は、 引数ポインタ・レジスタ、 構造体値のアドレス、 および、 関数へ引数を渡すのに使われる可能性のあるすべてのレジスタの内容を、 スタック上に割り当てられたメモリ・ブロックに待避し、 その後にそのブロックのアドレスを返します。
__builtin_apply (function, arguments, size)
この組み込み関数は、 arguments (void * 型) と size (int 型)によって記述されるパラメータのコピーを使って function (void (*)() 型) を呼び出します。 arguments の値は __builtin_apply_args によって返された値でなければなりません。 引数 size はスタック上の引数データのサイズをバイト単位で指定します。 この関数は、 function が返した値が何であれ、 その値を返す方法を記述するデータへの void * 型のポインタを返します。 そのデータは、 スタック上に割り当てられたメモリ・ブロックの中に待避されます。 size に指定するのに適切な値を計算することは、 常に簡単なことであるとは限りません。 この値は、 入力引数の領域からコピーされてスタックにプッシュされるべきデータの量を計算するために、 __builtin_apply によって使われます。
__builtin_return (result)
この組み込み関数は、 result により記述される値を戻り値として、 この組み込み関数を包含している関数から復帰します。 result には、 __builtin_apply から返却された値を指定しなければなりません。

式の型に対する名前付け

初期化子(initializer)とともに typedef 宣言を使うことで、 式の型に名前を与えることができます。 以下に、 式 exp の型の名前として name を定義する方法を示します。

typedef name = exp;

これは、 式の中に複数の文を持たせる機能と一緒に使うと役に立ちます。 以下に、 任意の算術型に対して作用する安全な「最大値を返す」マクロを定義するのに、 これら2つの機能を一緒に使う方法を示します。

#define max(a,b) \
  ({typedef _ta = (a), _tb = (b);  \
    _ta _a = (a); _tb _b = (b);     \
    _a > _b ? _a : _b; })

局所変数に対してアンダースコアで始まる名前を使う理由は、 ab が置き換えられる式の中で使われている変数の名前との衝突を回避するためです。 最終的には、 初期化子の後ろからスコープが開始されるような変数を宣言することを可能にする、 新しい形式の宣言構文を設計したいと考えています。 こちらの方が、 名前の衝突を防ぐ手段としてはより信頼性のあるものになるでしょう。

typeof による型への参照

式の型を参照する別の方法に typeof があります。 このキーワードを使うときの構文は sizeof の構文に似ていますが、 意味論的には typedef により定義された型名のような働きをします。

typeof への引数を記述する方法は2つあります。 1つは式を使う方法で、もう1つは型を使う方法です。 以下に、 式を使う方法の例を示します。

typeof (x[0](1))

ここでは、 x が関数の配列であると仮定しています。 この例によって記述される型は、 関数の戻り値の型です。

次に、 型名を引数に使う例を示します。

typeof (int *)

この例では、 記述される型は int へのポインタ型です。

ANSI C のプログラムに組み込まれた時に問題なく使えなければならないヘッダ・ファイルを書いている場合は、 typeof の代わりに __typeof__ と書いてください。 See section 代替キーワード

typeof は、 typedef が使えるところであればどこでも使うことができます。 例えば、 宣言、 キャスト、 sizeof の中、 typeof の中でそれを使うことができます。

一般化されたロケータ値

複合式(compound expression)、 条件式、 および、 キャストは、 そのオペランドがロケータ値(lvalue)である限り、 ロケータ値として使うことができます。 このことは、 これらのアドレスを取ったり、 その中に値を格納したりすることができるということを意味しています。

標準 C++ では、 複合式と条件式をロケータ値として使うことができますし、 参照型に対するキャストを使うこともできます。 したがって、 この拡張機能を C++ で書かれたソース・コードにおいて使用することには賛成できません。

複合式の中の最後の式がロケータ値であれば、 複合式に対して値を代入することができます。 以下の2つの式は同等です。

(a, b) += 5
a, (b += 5)

同様に、 複合式のアドレスを取ることもできます。 以下の2つの式は同等です。

&(a, b)
a, &b

条件式は、 その型が void ではなく、 かつ、 真のときに分岐する部分と偽のときに分岐する部分がともに正当なロケータ値であれば、 正当なロケータ値です。 例えば、 以下の2つの式は同等です。

(a ? b : c) = 5
(a ? b = 5 : (c = 5))

キャストは、 そのオペランドがロケータ値であれば、 正当なロケータ値です。 左辺側にキャストが使われる単純代入は、 まず右辺側をキャストで指定された型に変換した後に、 左辺側のキャストが実行される前の式の型に変換することによって実現されます。 値が格納された後に、 代入の型に合うように、 その値はキャストで指定された型に再変換されます。 したがって、 a の型が char * である場合、 次の2つの式は同等です。

(int)a = 5
(int)(a = (char *)(int)5)

`+=' のような算術演算を伴う複合代入がキャストに対して適用された場合、 キャストにより変換される型を使って算術演算が実行され、 そのあとは前のケースと同様に実行されます。 したがって、 以下の2つの式は同等です。

(int)a += 5
(int)(a = (char *)(int) ((int)a + 5))

ロケータ値に対するキャストのアドレスを取ることはできません。 なぜなら、 そのようなアドレスは整合性のある使い方ができないからです。 仮に、 f の型が float である場合に、 &(int)f によってアドレスを取ることができるとしましょう。 すると以下の文は、 浮動小数点値が入るべき箇所に、 整数値のビット・パターンを格納しようとすることになります。

*&(int)f = 1;

これは、 (int)f = 1 の動作とはまったく異なります。 こちらは、 1 を浮動小数点値に変換してから格納します。 このような不整合をもたらすよりも、 キャストに対する `&' の使用を禁止するほうが良いと考えました。

どうしても f のアドレスを int * 型のポインタにしたいのであれば、 単に (int *)&f と書くことができます。

オペランドの省略された条件式

条件式の真ん中のオペランドは省略することができます。 この場合、 最初のオペランドの値が 0 以外であれば、 その値が条件式の値そのものになります。

したがって、 条件式

x ? : y

は、 x の値が 0 以外であれば x の値を取り、 それ以外の場合は y の値を取ります。

この例は、 以下とまったく同等です。

x ? x : y

このような単純なケースでは、 真ん中のオペランドを省略できても特に役には立ちません。 これが役に立つのは、 最初のオペランドが副作用を実際に持つ場合、 あるいは、 (それがマクロ引数であって)副作用を持つ可能性がある場合です。 このような場合に、 最初のオペランドを真ん中に繰り返し記述すると、 副作用が2回働くことになります。 真ん中のオペランドを省略すれば、 同一のオペランドを再評価することによる望ましくない結果をもたらすことなく、 既に評価済みの値を再利用することになります。

ダブル・ワード整数

GNU C は、 int の2倍の長さを持つ整数のためのデータ型をサポートしています。 有符号整数については long long int、 無符号整数については unsigned long long int と書きます。 型が long long int の整数定数を作成するためには、 整数値の後ろに接尾語 LL を付けます。 型が unsigned long long int の整数定数を作成するためには、 整数値の後ろに接尾語 ULL を付けます。

これらの型は、 他の任意の整数型と同じように算術演算で使用することができます。 これらの型に対する加算、 減算、 ビット単位のブール演算は、 すべての種類のマシン上においてオープン・コード化されます(open-coded)。 乗算は、 ワードの乗算結果をダブル・ワードにまで拡張できるような乗算命令をサポートするマシンであれば、 オープン・コード化されます。 除算とシフト演算は、 そのための特別なサポートを提供するマシン上でのみオープン・コード化されます。 オープン・コード化されない演算は、 GNU CC に付属している特別なライブラリ・ルーチンを使用します。

関数プロトタイプを宣言せずに、 関数の引数に long long 型を使うと、 落し穴にはまる可能性があります。 ある関数がその引数に int 型を期待している場合に long long int 型の値を渡すと、 呼び出し側とサブルーチン側とで引数のバイト数の解釈に食い違いが生じるために、 混乱が発生することになります。 関数が long long int を期待しているところに int を渡す場合も、 同様の事態が発生します。 このような問題を回避するのに最良の方法はプロトタイプを使うことです。

複素数

GNU C は複素数のデータ型をサポートしています。 複素整数型と複素浮動小数点型の両方を、 キーワード __complex__ を使って宣言することができます。

例えば、 `__complex__ double x;' は、 実数部と虚数部の両方が double 型である変数として x を宣言します。 `__complex__ short int y;' は、 実数部と虚数部が short int 型である変数として y を宣言します。 このような例は役に立ちそうにはありませんが、 複素数型の集合がそろっていることを示しています。

複素数データ型の定数を記述するには、 接尾語 `i' もしくは `j' を使います (両者は同等ですので、どちらを使っても構いません)。 例えば、 2.5fi の型は __complex__ float であり、 3i の型は __complex__ int です。 このような定数は常に純粋な虚数値ですが、 実数定数に虚数定数を加算することにより、 任意の複素数値を作ることができます。

複素数値を持つ式 exp から実数部を抽出するには、 __real__ exp と書きます。 同様に、 虚数部を抽出するには __imag__ を使います。

演算子 `~' は、 複素数型の値に対して使われると、 その複素数の共役複素数を作ります。

GNU CC は、 複素数の自動変数を非連続的な形で割り当てることができます。 実数部をレジスタに置き、虚数部をスタック上に置くこと (あるいはその逆) さえ可能です。 サポートされるデバッグ情報形式のどれも、 このような非連続的な割り当てを表現する方法を提供していません。 そのため GNU CC は、 非連続的な複素数変数を、 あたかもそれが2つの別々の非複素数変数であるかのように記述します。 変数の実際の名前が foo であるとすると、 2つの架空の変数には foo$realfoo$imag という名前が与えられます。 デバッガを使って、 これら2つの架空の変数の値を調べたり、 値を設定したりすることができます。

将来の GDB バージョンでは、 こうした実数部と虚数部のペアを認識できるようになり、 それらを複素数型の単一の変数として扱うようになるでしょう。

長さ 0 の配列

GNU C では長さが 0 の配列を使うことができます。 長さが 0 の配列は、 構造体の最後の要素として大変役に立ちます。 それは実際には、 可変長オブジェクトのヘッダとなります。

struct line {
  int length;
  char contents[0];
};

{
  struct line *thisline = (struct line *)
    malloc (sizeof (struct line) + this_length);
  thisline->length = this_length;
}

標準 C では、 contents の長さを 1 にしなければなりません。 このことは、 領域を無駄に消費するか、 もしくは、 malloc への引数が複雑になるということを意味しています。

可変長配列

GNU C では可変長の自動配列を使うことができます。 可変長自動配列の宣言は他の任意の自動配列の宣言と似ていますが、 指定される長さが定数式ではないところが違います。 記憶域は、 配列が宣言されたところで割り当てられ、 その宣言を包含する波括弧 {} (brace-level)が終了したところで解放されます。 以下に例を示します。

FILE *
concat_fopen (char *s1, char *s2, char *mode)
{
  char str[strlen (s1) + strlen (s2) + 1];
  strcpy (str, s1);
  strcat (str, s2);
  return fopen (str, mode);
}

配列名のスコープからジャンプ等で抜け出ると、 その記憶域は解放されます。 スコープの外から中へジャンプして入り込むことはできません。 このようなことをするとエラー・メッセージが出力されます。

関数 alloca を使って、 可変長配列とほとんど同様の結果を実現することができます。 関数 alloca は、 他の多くの C の実装でも利用可能です (すべての C の実装で利用可能な訳ではありません)。 とはいうものの、 可変長配列の方がよりエレガントです。

これら2つの方法には、 ほかにも相違点があります。 alloca によって割り当てられた領域は、 alloca の呼び出しを包含する 関数 が復帰するまで存在します。 可変長配列の領域は、 配列名のスコープが終了するとすぐに解放されます (同一の関数の中で可変長配列と alloca の両方を使うと、 可変長配列の領域が解放される時に、 その可変長配列が割り当てられたあとに alloca によって割り当てられた領域もすべて解放されます)。

可変長配列は関数への引数としても使うことができます。

struct entry
tester (int len, char data[len][len])
{
  ...
}

配列の長さは、 記憶域が割り当てられる時に一度だけ計算されます。 その長さは、 sizeof が使われる場合に備えて、 配列のスコープが終了するまで記憶されます。

パラメータ・リストで配列を先に渡し、 そのあとで長さを渡したい場合には、 パラメータ・リストにおける前方宣言(forward declaration)を使うことができます。 これもまた、GNU の拡張機能です。

struct entry
tester (int len; char data[len][len], int len)
{
  ...
}

セミコロンの前の `int len'パラメータの前方宣言です。 その役割は、 data の宣言が解析される時に len を既知の名前にすることです。

このようなパラメータの前方宣言は、 パラメータ・リストにおいて何個でも書くことができます。 個々の前方宣言はカンマもしくはセミコロンにより区切ることができますが、 最後の前方宣言だけはセミコロンで終わらなければなりません。 この最後のセミコロンのあとに「真の」パラメータ宣言が続きます。 個々の前方宣言と「真の」宣言とは、 パラメータの名前とデータ型の点で一致していなければなりません。

可変個数の引数を持つマクロ

GNU C におけるマクロは、 関数とほぼ同様に、 可変個数の引数を受け取ることができます。 マクロの定義構文は関数の定義に使われるものとほぼ同様です。 以下に例を示します。

#define eprintf(format, args...)  \
 fprintf (stderr, format , ## args)

ここでは args可変引数(rest argument)です。 それは、 マクロ呼び出しに含まれるのと同数の、 0(ゼロ)個以上の引数を取ります。 マクロ呼び出しに含まれる引数すべてと個々の引数の間を区切るカンマとが、 args の値になります。 マクロ本体の中で args が使われている部分は、 この値によって置き換えられます。 したがって、 以下のような展開が行われることになります。

eprintf ("%s:%d: ", input_file_name, line_number)
==>
fprintf (stderr, "%s:%d: " , input_file_name, line_number)

文字列定数の後ろのカンマは eprintf の定義から取られているのに対して、 最後のカンマは args の値から取られていることに注意してください。

`##' を使っているのは、 args の部分に相当する引数が存在しないような状況に対処するためです。 このような場合、 args の値は空であり、 定義の中の2番目のカンマが邪魔になります。 このカンマがマクロの展開後に残ると、 以下のような結果になります。

fprintf (stderr, "success!\n" , )

これは C の構文としては不当です。 `##' を使うとカンマが除去されるので、 以下のような結果になります。

fprintf (stderr, "success!\n")

これは GNU C プリプロセッサの特別な機能です。 空の可変引数の前の `##' は、 `##' の前にある空白類以外の文字の並びをマクロ定義から除去します (別のマクロ引数が前にある場合は、それらの引数は除去されません)。

前にある空白類以外の文字の並びのうち最後のものを除去するよりも、 最後のプリプロセッサ・トークンを除去する方が望ましいかもしれません。 実際、 この機能をいつかそのように変更するかもしれません。 この機能の定義が変更されたあとでも実際上の意味が変わらないようにするために、 前にある空白類以外の文字の並びが単一のトークンであるようにマクロ定義を記述することをお勧めします。

ロケータ値ではない配列の添字

ロケータ値(lvalue)ではない配列に対して添字を使うことができます。 ただし、単項演算子 `&' は使えません。 例えば、 以下の例は GNU C では正当ですが、 他の C では正当ではありません。

struct foo {int a[4];};

struct foo f();

bar (int index)
{
  return f().a[index];
}

void 型へのポインタと関数ポインタに対する算術演算

GNU C では、 void 型へのポインタと関数ポインタに対する加算と減算がサポートされています。 これは、 void のサイズと関数のサイズを 1 として扱うことで実現されています。 その結果として、 void 型と関数型に対する sizeof も使うことができ、 これらは値 1 を返します。

`-Wpointer-arith' オプションは、 これらの拡張機能が使われている場合に警告メッセージを出力するよう要求するものです。

定数以外の初期化子

標準 C++ と同様に GNU C でも、 自動変数に割り当てられた集合体の初期化子の要素は、 定数式である必要はありません。 以下に、 実行時に変化する要素を使った初期化子の例を示します。

foo (float f, float g)
{
  float beat_freqs[2] = { f-g, f+g };
  ...
}

生成関数式

GNU C は生成関数式(constructor expression)をサポートしています。 生成関数式の外見は、 初期化子を持つキャストのようなものです。 その値は、 キャストで指定された型のオブジェクトで、 初期化子で指定された要素を持ちます。

通常、 指定される型は構造体です。 struct foostructure とが以下に示すように宣言されているものとしましょう。

struct foo {int a; char b[2];} structure;

以下に、 struct foo を生成関数式によって生成する例を示します。

structure = ((struct foo) {x + y, 'a', 0});

これは以下のように書くのと同等です。

{
  struct foo temp = {x + y, 'a', 0};
  structure = temp;
}

配列を生成することもできます。 生成関数式のすべての要素が、 初期化子の中で使うのに適する単純な定数式である (あるいは、 単純な定数式から構成される) のであれば、 その生成関数式はロケータ値(lvalue)であり、 以下に示すように、 その最初の要素へのポインタに強制的に型変換することができます。

char **foo = (char *[]) { "x", "y", "z" };

その要素が単純な定数ではないような配列生成関数式は、 あまり役に立ちません。 というのは、 そのような生成関数式はロケータ値ではないからです。 それを正当に使う方法は2つしかありません。 配列への添字付けを行う場合と配列変数の初期化を行う場合です。 前者はおそらく switch 文よりも処理速度が遅くなるでしょう。 また後者は、 通常の C の初期化子が行うことと同一のことを行うだけです。 以下に、 配列生成関数式に添字付けを行う例を示します。

output = ((int[]) { 2, x, 28 }) [input];

スカラ(scalar)型と共用体型に対する生成関数式も使うことができますが、 この場合の生成関数式はキャストと同等になります。

初期化子におけるラベル付き要素

標準 C では、 初期化子の要素の現れる順序は固定であり、 初期化される配列や構造体の要素の順序と同一である必要があります。

GNU C では、 要素が適用される配列のインデックス、 もしくは、 構造体のフィールド名を指定することによって、 任意の順序で要素を並べることができます。 この拡張機能は GNU C++ では実装されていません。

配列のインデックスを指定するには、 要素の値の前に `[index]' もしくは `[index] =' と書きます。 以下に例を示します。

int a[6] = { [4] 29, [2] = 15 };

これは以下と同等です。

int a[6] = { 0, 0, 15, 0, 29, 0 };

初期化される配列が自動変数に割り当てられている場合でも、 インデックスの値は定数式でなければなりません。

ある範囲の要素を同一の値で初期化するには、 `[first ... last] = value' のように書きます。 以下に例を示します。

int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };

インデックスとして指定された値のうち最大のものに 1 を加えた値が配列の長さになることに注意してください。

構造体の初期化子の場合は、 要素の値の前に `fieldname:' と書くことによって、 初期化するフィールドの名前を指定します。 例えば、 以下のような構造体が与えられているとしましょう。

struct point { int x, y; };

このとき、

struct point p = { y: yvalue, x: xvalue };

という初期化は、以下と同等です。

struct point p = { xvalue, yvalue };

同一の意味を持つ別の構文として、 `.fieldname =' があります。 以下に例を示します。

struct point p = { .y = yvalue, .x = xvalue };

共用体を初期化するときに、 初期化の対象となる共用体の要素を指定するのに、 要素ラベルを使うことができます (コロンを使う構文とピリオドと等号を使う構文のどちらでも使えます)。 例えば、

union foo { int i; double d; };

union foo f = { d: 4 };

は、 4 を double 型に変換して、 共用体の2番目の要素を使って共用体の中に格納します。 これとは対照的に、 4 をキャストして union foo 型に変換すると、 4 は整数なので、 共用体の中の整数 i として格納されることになります (See section 共用体型へのキャスト)。

要素に名前をつけるテクニックを、 通常の C における連続した要素の初期化と組み合わせることができます。 初期化子の要素のうちラベルを持たないものは、 配列や構造体の連続した要素の中の次の要素に適用されます。 例えば、

int a[6] = { [1] = v1, v2, [4] = v4 };

は、以下と同等です。

int a[6] = { 0, v1, v2, 0, v4, 0 };

配列の初期化子の要素にラベルをつけるのは、 配列のインデックスが文字や列挙型の値である場合に特に役に立ちます。 以下に例を示します。

int whitespace[256]
  = { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
      ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };

case 文における範囲指定

単一の case ラベルの中で、 ある連続した値の範囲を指定することができます。 以下に例を示します。

case low ... high:

これは、 low 以上 high 以下の個々の整数値について、 個別に case ラベルを記述するのと同等の効果を持ちます。

この機能は、 ASCII 文字コードの範囲を指定するのに特に役に立ちます。

case 'A' ... 'Z':

注意: ...の前後には空白を書いてください。 そうしないと、 整数値を使う場合に正しく解析されない可能性があります。 例えば、

case 1 ... 5:

のように書いてください。

case 1...5:

のようには書かないでください。

共用体型へのキャスト

共用体型へのキャストは、 指定される型が共用体であるという点を除けば、 他のキャストと似ています。 型は、 union tag によって指定することもできますし、 あるいは、 typedef された名前によって指定することもできます。 ただし、 共用体へのキャストは、 実際にはキャストではなく生成関数です。 したがって、 通常のキャストのようにロケータ値(lvalue)になることはありません (See section 生成関数式)。

ある共用体型へキャストすることができる型は、 その共用体のメンバの型です。 よって、 以下のような共用体と変数が与えられた場合、

union foo { int i; double d; };
int x;
double y;

xy は両方とも union foo 型へキャストすることができます。

共用体型の変数に対する代入式の右辺側においてキャストを使うことは、 その共用体のメンバに値を格納するのと同等です。

union foo u;
...
u = (union foo) x  ==  u.i = x
u = (union foo) y  ==  u.d = y

共用体へのキャストを関数への引数として使うこともできます。

void hack (union foo);
...
hack ((union foo) x);

関数属性の宣言

GNU C では、 プログラムの中で呼び出される関数に関して特定の情報を宣言することによって、 コンパイラが、 関数呼び出しをより良く最適化したり、 ソース・コードをより綿密にチェックしたりすることができるようになります。

キーワード __attribute__ によって、 宣言をする際に特別な属性を指定することができます。 このキーワードの後ろに、 2重の丸括弧 (()) に囲まれた属性指定が続きます。 現在、 8個の属性 noreturnconstformatsectionconstructordestructorunusedweak が関数に対して定義されています。 section を含むその他の属性が、 変数宣言 (see section 変数属性の指定) と型 (see section 型属性の指定) においてサポートされています。

個々のキーワードの前後に `__' を付けて属性を指定することもできます。 これにより、 同じ名前を持つマクロが定義済みであるような事態を心配することなく、 ヘッダ・ファイルの中でキーワードを使うことができるようになります。 例えば、 noreturn の代わりに __noreturn__ を使うことができます。

noreturn
標準ライブラリ関数の中には、 abortexit のように、 復帰(return)することができないものが少しあります。 何もしなくても、 GNU CC はこれらの関数が復帰しないということを知っています。 プログラムによっては、 決して復帰しない独自の関数を定義するものがあります。 このような関数を noreturn と宣言することによって、 その関数が復帰しないということをコンパイラに知らせることができます。 以下に例を示します。
void fatal () __attribute__ ((noreturn));

void
fatal (...)
{
  ... /* エラー・メッセージを表示する */ ...
  exit (1);
}
noreturn キーワードは、 fatal が復帰することがないものと想定するようコンパイラに通知します。 コンパイラは、 fatal が万一復帰した場合に発生する事態のことはかまわずに、 最適化を行うことができます。 これにより、 少し良いバイナリ・コードが生成されます。 より重要なことは、 初期化されていない変数についての見せかけだけで意味のない警告メッセージを回避するのに役立つということです。 呼び出し側の関数によって待避されるレジスタが、 noreturn 属性を持つ関数を呼び出す前に復元されると想定してはなりません。 noreturn 属性を持つ関数が void 以外の型の戻り値を持つのは無意味なことです。 noreturn 属性は、 バージョン 2.5 より前のバージョンの GNU C では実装されていません。 ある関数が復帰しないということを宣言する方法で、 カレント・バージョンといくつかの古いバージョンでうまく機能するものに、 以下のような方法があります。
typedef void voidfn ();

volatile voidfn fatal;
const
多くの関数は、 その関数に渡される引数の値を一切参照せず、 その関数の戻り値以外に何の作用も持ちません。 このような関数には、 算術演算子と同様に、 共通する部分式の除去やループの最適化を適用することができます。 このような関数は、 const 属性を指定して宣言するべきです。 例えば、

int square (int) __attribute__ ((const));
は、 この square という名前の仮の関数が、 プログラムの中で指定された回数よりも少ない回数しか呼び出さなくても安全であるということを表しています。 const 属性は、 バージョン 2.5 より前のバージョンの GNU C では実装されていません。 ある関数が副作用を持たないということを宣言する方法で、 カレント・バージョンといくつかの古いバージョンでうまく機能するものに、 以下のような方法があります。
typedef int intfn ();

extern const intfn square;
この方法は、 2.6.0 以降の GNU C++ ではうまく機能しません。 C++ の言語仕様では、 `const' は戻り値に対して指定しなければならないとなっているからです。 引数としてポインタを取り、 そのポインタが指し示すデータを参照する関数には、 const 属性を宣言してはなりません。 同様に、 const 属性を持たない関数を呼び出す関数には、 通常 const 属性を宣言してはなりません。 const 属性を持つ関数が void 型の戻り値を返すのは無意味なことです。
format (archetype, string-index, first-to-check)
format 属性は、 その関数の取る引数が、 書式文字列に照らし合わせて型チェックが行われるべきである printfscanf のような方式の引数であることを指定します。 例えば、
extern int
my_printf (void *my_object, const char *my_format, ...)
      __attribute__ ((format (printf, 2, 3)));
という宣言を行うと、 コンパイラは、 printf 方式の書式文字列引数である my_format を使って、 my_printf の呼び出しにおける引数の整合性をチェックします。 archetype パラメータは、 書式文字列がどのように解釈されるかを決定するもので、 printfscanf のいずれかでなければなりません。 string-index パラメータは、 どの引数が書式文字列引数であるかを (1 から始まる数で) 指定します。 一方、 first-to-check は、 書式文字列に照らし合わせてチェックするべき引数のうち最初の引数の番号です。 (vprintf のように) チェックされるべき引数を指定できない関数に対しては、 第3パラメータに0(ゼロ)を指定してください。 この場合、 コンパイラは、 書式文字列だけを対象にして整合性のチェックを行います。 上の例では、 書式文字列 (my_format) は関数 my_printf の第2引数であり、 チェックするべき引数は第3引数から始まっています。 したがって、 format 属性の正しいパラメータは 2 と 3 です。 書式文字列を引数として取るユーザ独自の関数がある場合に、 その関数呼び出しにおける誤りを GNU CC がチェックできるように、 format 属性を使ってそのような関数であるということを指定することができます。 ANSI ライブラリ関数の printffprintfsprintfscanffscanfsscanfvprintfvfprintfvsprintf については、 (`-Wformat' を使って) そのような警告が要求されているときにはいつでも、 コンパイラは書式のチェックを行います。 したがって、 ヘッダ・ファイル `stdio.h' を修正する必要はありません。
format_arg (string-index)
format_arg 属性は、 その関数が printfscanf のような方式の引数を取り、 それを修正 (例えば、他の言語に翻訳) した後に、 printfscanf のような関数に渡すということを指定します。 例えば、
extern char *
my_dgettext (char *my_domain, const char *my_format)
      __attribute__ ((format_arg (2)));
という宣言を行うと、 コンパイラは、 my_dgettext の呼び出しにおける引数を、 printf 方式の書式文字列引数である my_format に照らし合わせ、 整合性のチェックを行います。 この my_dgettext の呼び出しの結果が、 printfscanf のような方式の関数に渡されます。 string-index パラメータは、 どの引数が書式文字列引数であるかを (1 から始まる数で) 指定します。 書式文字列を変更するようなユーザ独自の関数がある場合に、 format-arg 属性を使ってそのような関数であるということを指定することができます。 これにより、 printf 関数や scanf 関数のオペランドが、 そのような独自関数への呼び出しになっている場合に、 これをチェックすることができます。 コンパイラは常に、 gettextdgettextdcgettext をこのように取り扱います。
section ("section-name")
通常、 コンパイラは生成されたバイナリ・コードを text セクションに置きます。 しかし時には、 別のセクションを追加したり、 特定の関数を特別なセクションに置くようにすることが必要になることがあります。 section 属性は、 関数が特定のセクション内に置かれるよう指定します。 例えば、
extern void foobar (void) __attribute__ ((section ("bar")));
という宣言は、 関数 foobarbar セクションに置きます。 ファイル形式によっては、 セクションの任意指定がサポートされていないものもあるので、 section 属性はすべてのプラットフォームで利用可能な訳ではありません。 あるモジュールのすべての内容を特定のセクションにマップする必要がある場合には、 この属性ではなく、 リンカの機能を使うことを検討してください。
constructor
destructor
constructor 属性を指定された関数は、 main () 関数が実行される前に、 自動的に呼び出されるようになります。 同様に、 destructor 属性を指定された関数は、 main () の実行が完了した後、 もしくは、 exit () が呼び出された後に、 自動的に呼び出されるようになります。 これらの属性を持つ関数は、 プログラムが実行される間に暗黙のうちに使われるデータを初期化するのに役に立ちます。 これらの属性は、 現在のところ Objective C では実装されていません。
unused
この属性が関数に対して指定されると、 その関数はおそらく使われないはずであるという意味になります。 GNU CC は、 このような関数に対しては警告メッセージを出力しません。 C++ ではパラメータを持たない定義は正当なので、 現在のところ GNU C++ はこの属性をサポートしていません。
weak
weak 属性を指定された宣言は、 広域(global)シンボルではなく、弱い(weak)シンボルとして出力されるようになります。 これは主として、 ユーザのソース・コードの中で無効にすることのできるライブラリ関数を定義するのに役に立ちますが、 関数以外の宣言においても使うこともできます。 弱いシンボルは ELF ターゲットにおいてサポートされています。 また、 GNU アセンブラと GNU リンカを使っている場合は、 a.out ターゲットにおいてもサポートされています。
alias ("target")
alias 属性を指定された宣言は、 別のシンボルへの別名として出力されます。 その別のシンボルは指定されていなければなりません。 例えば、
void __f () { /* 何かを行う */; }
void f () __attribute__ ((weak, alias ("__f")));
は、 `f'`__f' への弱い(weak)別名として宣言します。 C++ では、 その別名が指し示すシンボルは、 マングルされた(mangled)名前で指定されなければなりません。 すべてのターゲット・マシンにおいてこの属性がサポートされている訳ではありません。
regparm (number)
Intel 386 上では、 regparm 属性により、 コンパイラは最高で number で指定される個数までの整数引数を、 スタックではなく EAXEDXECX レジスタに入れて渡すようになります。 可変個数の引数を取る関数は、 この属性を指定されても引き続きスタック上においてすべての引数を渡されます。
stdcall
Intel 386 上では、 stdcall 属性により、 関数が可変個数の引数を取るのでない限り、 引数を渡すのに使われたスタック領域は呼び出された関数がポップするものと、 コンパイラは想定するようになります。 Windows NT 用の PowerPC コンパイラは、 現在のところ stdcall 属性を無視します。
cdecl
Intel 386 上では、 cdecl 属性により、 引数を渡すのに使われたスタック領域は呼び出した関数がポップするものと、 コンパイラは想定するようになります。 これは、 `-mrtd' オプションの効果を無効にするのに役に立ちます。 Windows NT 用の PowerPC コンパイラは、 現在のところ cdecl 属性を無視します。
longcall
RS/6000 と PowerPC 上では、 longcall 属性により、 カレントな位置から 64 メガバイト (67,108,864 バイト) を超えて離れた位置にある関数でも呼び出すことができるよう、 コンパイラは常にポインタを使ってその関数を呼び出すようになります。
dllimport
Windows NT を実行している PowerPC 上では、 dllimport 属性により、 コンパイラは、 Windows NT の DLL により設定された関数ポインタを指し示す広域ポインタを使ってその関数を呼び出すようになります。 ポインタの名前は、 __imp_ と関数名を結合することにより作られます。
dllexport
Windows NT を実行している PowerPC 上では、 dllexport 属性により、 コンパイラは、 その関数を dllimport 属性を使って呼び出すことができるように、 関数ポインタへの広域ポインタを提供するようになります。 ポインタの名前は、 __imp_ と関数名を結合することにより作られます。
exception (except-func [, except-arg])
Windows NT を実行している PowerPC 上では、 exception 属性により、 コンパイラは、 宣言された関数のために出力する構造化された例外テーブルのエントリを変更するようになります。 except-func に指定される文字列もしくは識別子が 、 構造化された例外テーブルの第3のエントリに入れられます。 それは、 例外が発生した場合に例外処理メカニズムにより呼び出される関数を表します。 except-arg に文字列もしくは識別子が指定されると、 それは構造化された例外テーブルの第4のエントリに入れられます。
function_vector
このオプションは、 H8/300 と H8/300H 上において、 指定された関数が関数ベクタを利用して呼び出されるべきであることを示すのに使います。 関数ベクタを利用して関数呼び出しを行うとコード・サイズは小さくなります。 しかし、 関数ベクタのサイズには上限 ( H8/300 では 128 エントリ、 H8/300H では 64 エントリ) があり、 割り込みベクタとの間で領域が共用されます。 このオプションを正常に機能させるためには、 GNU binutils バージョン 2.7 以降の GAS と GLD を使用しなければなりません。
interrupt_handler
このオプションは、 H8/300 と H8/300H 上において、 指定された関数が割り込みハンドラであることを示すのに使います。 この属性が指定されていると、 コンパイラは、 その関数の入口と出口において、 割り込みハンドラの中で使うのに適した命令シーケンスを生成します。
eightbit_data
このオプションは、 H8/300 と H8/300H 上において、 指定された変数が8ビット・データ・セクションに置かれるべきであることを示すのに使います。 コンパイラは、 8ビット・データ域内のデータへの特定の操作に対して、 より効率的なコードを生成するようになります。 8ビット・データ域は、 256 バイトのデータまでに制限されていることに注意してください。 このオプションを正常に機能させるためには、 GNU binutils バージョン 2.7 以降の GAS と GLD を使用しなければなりません。
tiny_data
このオプションは、 H8/300H 上において、 指定された変数がタイニ(tiny)・データ・セクションに置かれるべきであることを示すのに使います。 コンパイラは、 タイニ(tiny)・データ・セクション内のデータのロード命令とストア命令に対して、 より効率的なコードを生成するようになります。 タイニ(tiny)・データ域は、 32K バイトより若干少ないデータまでに制限されていることに注意してください。
interrupt
このオプションは、 M32R/D 上において、 指定された関数が割り込みハンドラであることを示すのに使います。 この属性が指定されていると、 コンパイラは、 その関数の入口と出口において、 割り込みハンドラの中で使うのに適した命令シーケンスを生成します。
model (model-name)
この属性は、 M32R/D 上において、 オブジェクトのアドレス範囲(addressability)を設定し、 関数に対して生成されるバイナリ・コードを決定するのに使います。 識別子 model-namesmallmediumlarge のいずれかであり、 コード・モデルの1つを表します。 スモール・モデルのオブジェクトは (そのアドレスが ld24 命令によってロードできるように) メモリの低位 16MB の範囲内にあり、 bl 命令によって呼び出すことができます。 ミディアム・モデルのオブジェクトは、 32 ビットのアドレス空間の任意の位置にあり (そのアドレスをロードするために、 コンパイラは seth/add3 命令を生成します)、 bl 命令によって呼び出すことができます。 ラージ・モデルのオブジェクトは、 32 ビットのアドレス空間の任意の位置にあり (そのアドレスをロードするために、 コンパイラは seth/add3 命令を生成します)、 かつ、 bl 命令では到達不可能なことがあります (その場合、 コンパイラは、 はるかに遅い seth/add3/jl 命令シーケンスを生成します)。

2重の丸括弧 (()) の内部において属性をカンマで区切るか、 属性宣言の直後に別の属性宣言を記述することによって、 宣言の中において複数の属性を指定することができます。

ANSI C の #pragma を使うべきであるとして、 __attribute__ 機能に反対する人々がいます。 ANSI C の #pragma を使わない理由は2つあります。

  1. マクロから #pragma コマンドを生成することは不可能です。
  2. 同一の #pragma が異なるコンパイラにおいて持つ意味について確定的なことを言うことができません。

これら2つの理由は、 #pragma を使うよう提案されるであろうどのような場合においても、 適用できます。 どのような場合においても#pragma を使うのは基本的に誤りです。

プロトタイプと古い方式の関数定義

GNU C は、 関数プロトタイプによって、 それよりも後ろにある、 プロトタイプを使わない古い方式の定義を無効にすることができるように、 ANSI C を拡張しています。 以下の例を考えてみてください。

/* コンパイラが旧式のものでなければプロトタイプを使う  */
#ifdef __STDC__
#define P(x) x
#else
#define P(x) ()
#endif

/* プロトタイプ関数宣言  */
int isroot P((uid_t));

/* 古い方式の関数定義  */
int
isroot (x)   /* ??? lossage here ??? */
     uid_t x;
{
  return x == 0;
}

uid_t がたまたま short であるとしましょう。 ANSI C ではこの例は許されません。 というのは、 プロトタイプを使わない古い方式の定義におけるサブワード(subword)引数が汎整数拡張されるからです。 そのため、 この例においては、 関数定義の引数は実際には int であり、 プロトタイプにおける引数の型である short と一致しません。

この ANSI C の制限により、 伝統的な C コンパイラとの間で移植性のあるソース・コードを記述するのが困難になります。 というのは、 プログラマには uid_t の型が shortintlong のいずれであるかが分からないからです。 そこで GNU C では、 このような場合に、 プロトタイプがそれより後ろにある古い方式の定義を無効にすることができるようにしています。 より正確に言うと、 GNU C では、 関数プロトタイプにおける引数の型と、 それよりも後ろにある古い方式の定義によって指定される引数の汎整数拡張される前の型とが同一である場合、 前者が後者の汎整数拡張後の型を無効にします。 よって GNU C においては、 上記の例は以下と同等となります。

int isroot (uid_t);

int
isroot (uid_t x)
{
  return x == 0;
}

GNU C++ は古い方式の関数定義をサポートしていないので、 この拡張機能とは無関係です。

C++ 方式のコメント

GNU C では、 `//' により始まり行末まで続く C++ 方式のコメントを使うことができます。 他の多くの C の実装でもこのようなコメントを使うことができます。 これが将来の C の標準仕様に含まれるのは、 ありそうなことです。 しかし、 `-ansi' もしくは `-traditional' を指定した場合には、 C++ 方式のコメントは認識されません。 これは、 C++ 方式のコメントが、 dividend//*comment*/divisor のような伝統的な C における書き方と両立しないからです。

識別子の名前の中のドル記号

GNU C では、 通常、 識別子の名前の中でドル記号を使うことができます。 これは、 C の多くの伝統的な実装においても、 そのような識別子を使うことができるからです。 しかし、 識別子の中のドル記号がサポートされないターゲット・マシンも少しあります。 その典型的な理由は、 ターゲット・アセンブラが識別子の中のドル記号を許さないというものです。

定数の中の ESC 文字

ASCII 文字 ESC を表すために、 文字列もしくは文字定数の中において、 `\e' という文字の並びを使うことができます。

型もしくは変数の境界整列の調査

キーワード __alignof__ によって、 あるオブジェクトがどのように境界整列(alignment)されるか、 もしくは、 ある型が通常必要とする最小の境界整列の値は何かを調べることができます。 その構文は、 sizeof と同様です。

例えば、 あるターゲット・マシン上では、 double 型の値は 8 バイト境界に境界整列されなければならないとしましょう。 すると、 __alignof__ (double) は 8 になります。 多くの RISC マシンでは実際こうなります。 より伝統的なマシン・デザインにおいては、 __alignof__ (double) は 4 ですが、 2 の場合もあります。

マシンによっては、 実際にまったく境界整列を必要としないものもあります。 そのようなマシンでは、 奇数アドレスにおいて任意のデータ型を参照することさえ許されます。 このようなマシンでは、 __alignof__ は型の推奨境界整列を報告します。

__alignof__ のオペランドが型ではなくロケータ値(lvalue)の場合は、 __alignof__ の値は、 そのロケータ値が取ると分かっている境界整列の値のうち最大の値となります。 この境界整列の値は、 そのロケータ値のデータ型から決められることもありますし、 そのロケータ値が構造体の一部である場合には、 その構造体から境界整列の値を継承することもあります。 例えば、

struct foo { int x; char y; } foo1;

という宣言のあとでは、 foo1.y のデータ型自体は境界整列を一切必要としないにもかからわず、 __alignof__ (foo1.y) の値はおそらく __alignof__ (int) と同じ値の 2 か 4 になります。

オブジェクトの境界整列を指定する方法を提供してくれる関連機能に __attribute__ ((aligned (alignment))); があります。 これについては、 次のセクションを参照してください。

変数属性の指定

キーワード __attribute__ により、 変数もしくは構造体フィールドの特別な属性を指定することができます。 このキーワードの後ろに、 2重の丸括弧 (()) に囲まれた属性指定が続きます。 現在、 8個の属性 alignedmodenocommonpackedsectiontransparent_unionunusedweak が変数に対して定義されています。 その他の属性が、 関数(see section 関数属性の宣言)および型(see section 型属性の指定)に対して利用可能です。

個々のキーワードの前後に `__' を付けて属性を指定することもできます。 これにより、 同じ名前を持つマクロが定義済みであるような事態を心配することなく、 ヘッダ・ファイルの中でキーワードを使うことができるようになります。 例えば、 aligned の代わりに __aligned__ を使うことができます。

aligned (alignment)
この属性は、 変数もしくは構造体フィールドの最小の境界整列の値をバイト単位で指定します。 例えば、
int x __attribute__ ((aligned (16))) = 0;
という宣言があると、 コンパイラは広域変数 x を 16 バイト境界に割り当てます。 68040 では、 16 バイト境界に整列されたオペランドを必要とする move16 命令にアクセスするのに、 これを asm 式と一緒に使うことができます。 構造体フィールドの境界整列を指定することもできます。 例えば、 ダブル・ワード境界に整列された int のペアを作成するには、 以下のように書くことができます。
struct foo { int x[2] __attribute__ ((aligned (8))); };
これは、 共用体を作成して、 その共用体を強制的にダブル・ワード境界に整列させる double 型のメンバを持たせるという方法の代わりになります。 関数の境界整列を指定することはできません。 関数の境界整列は、 マシンの要件によって決定されるものであって、 それを変更することはできません。 typedef 名は、 単なる別名であって別個の型ではないので、 その境界整列を指定することはできません。 前の例に示されるように、 ある与えられた変数もしくは構造体フィールドに対してコンパイラに使ってもらいたい境界整列を (バイト単位で) 明示的に指定することができます。 あるいは、 境界整列の係数(factor)を省略することによって、 コンパイルのターゲット・マシンにとって有意味な最大の境界整列に変数もしくはフィールドを境界整列するよう、 コンパイラに要求することもできます。 例えば、 以下のように書くことができます。
short array[3] __attribute__ ((aligned));
aligned 属性の指定において境界整列の係数を省略した場合にはいつでも、 コンパイルのターゲット・マシン上において任意のデータ型に対して使用される境界整列の最大値を、 宣言された変数もしくはフィールドの境界整列の値として、 コンパイラが自動的に設定します。 このようにするとしばしば、 コピー操作がより効率的になります。 というのは、 このようにして境界整列された変数もしくはフィールドとの間でコピー処理を実行する場合に、 一度にコピーできるメモリ上のデータ量が最大の命令をコンパイラが選択して使うことができるからです。 aligned 属性は、 境界整列の値を大きくすることしかできません。 しかし、 packed 属性を指定することによって、 その値を小さくすることも可能です。 これについては、後に説明します。 リンカに固有の制限により、 aligned 属性の効果が制限されるかもしれないことに注意してください。 多くのシステムにおいて、 リンカは、 ある特定の境界整列の値を上限として、 その範囲内においてしか変数の境界整列を行うことができません (リンカによっては、 サポートされる境界整列の最大値が非常に小さいものもあります)。 使っているリンカが、 最大でも 8 バイト境界までしか変数の境界整列を行えないのであれば、 __attribute__ において aligned(16) を指定しても、 実際には 8 バイト境界までしか実現できません。 より詳しい情報については、 リンカのドキュメントを参照してください。
mode (mode)
この属性は、 宣言のデータ型を指定します。 指定される型は、 モード mode に対応する型です。 これを使うと、 事実上、 mode の長さ(width)に応じた整数型もしくは浮動小数点型を要求することになります。 モードが1バイトの整数型に対応することを示すために、 モードとして `byte' もしくは `__byte__' を指定することもできます。 同様に、 1ワードの整数型に対応するモードについては `word' もしくは `__word__' を、 ポインタを表すために使われるモードについては `pointer' もしくは `__pointer__' を、 それぞれ指定することができます。
nocommon
この属性は、 変数を「共通ブロック」に置かず、 その変数のための領域を直接割り当てるよう、 GNU CC に対して要求します。 `-fno-common' フラグを指定すると、 GNU CC はすべての変数に対してこのような処理を実行します。 変数に対して nocommon 属性を指定すると、 その変数は0(ゼロ)により初期化されます。 変数は、 1つのソース・ファイルの中でのみ初期化することができます。
packed
packed 属性は、 aligned 属性によってより大きな値が指定されていない限り、 変数もしくは構造体フィールドが、 可能な境界整列の値のうち最小の値を取るよう指定します。 この最小値は、 変数については1バイトであり、 フィールドについては1ビットです。 以下に示す構造体では、 フィールド x には packed 属性が指定されているため、 a の直後に置かれます。
struct foo
{
  char a;
  int x[2] __attribute__ ((packed));
};
section ("section-name")
コンパイラは通常、 生成するオブジェクトを databss といったセクションに置きます。 しかし時には、 例えば特殊なハードウェアにマップするために、 追加のセクションが必要になったり、 特定の変数を特殊なセクションに置くことが必要になったりします。 section 属性は、 ある変数 (もしくは関数) が、 ある特定のセクション内に存在するよう指定します。 例えば、 以下の小さなプログラムでは、 いくつかの特別なセクション名を使っています。
struct duart a __attribute__ ((section ("DUART_A"))) = { 0 };
struct duart b __attribute__ ((section ("DUART_B"))) = { 0 };
char stack[10000] __attribute__ ((section ("STACK"))) = { 0 };
int init_data __attribute__ ((section ("INITDATA"))) = 0;

main()
{
  /* スタック・ポインタを初期化する */
  init_sp (stack + sizeof (stack));

  /* 初期データを初期化する */
  memcpy (&init_data, &data, &edata - &data);

  /* シリアル・ポートを始動する */
  init_duart (&a);
  init_duart (&b);
}
この例に示されているように、 広域変数の初期化定義のところで section 属性を使ってください。 初期化されない変数宣言のところで section 属性を使うと、 GNU CC は警告メッセージを出力するか、 そうでなければ、 この属性を無視します。 リンカの動作の仕方のために、 完全に初期化が行われる広域定義においてしか section 属性を使うことはできません。 リンカは、 個々のオブジェクトが1度だけ定義されることを要求しますが、 例外として、 初期化されない変数は試験的に common (もしくは bss) セクションに配置され、 複数回「定義」されることができます。 `-fno-common' フラグもしくは nocommon 属性を使うことで、 強制的に変数を初期化させることができます。 ファイル形式によっては、 セクションの任意指定をサポートしていないものもありますので、 section 属性はすべてのプラットフォーム上で利用可能な訳ではありません。 あるモジュールのすべての内容を特定のセクションにマップする必要がある場合には、 この属性ではなく、 リンカの機能を使うことを検討してください。
transparent_union
共用体型の関数パラメータに対して指定されるこの属性は、 そのパラメータに対応する引数自体はその共用体の任意のメンバの型を持つことができるが、 その引数がその関数に渡される際には、 共用体の1番目のメンバの型を持つものとして扱われるということを意味します。 詳細については、 See section 型属性の指定 を参照してください。 共用体データ型に対する typedef にこの属性を使うこともできます。 この場合、 その型を持つすべての関数パラメータに対して、 この属性が適用されます。
unused
変数に対して指定されるこの属性は、 その変数はおそらく使われないはずであるということを意味します。 GNU CC は、 その変数については警告メッセージを出力しません。
weak
weak 属性については、 See section 関数属性の宣言 において説明されています。
model (model-name)
この属性は、 M32R/D 上においてオブジェクトのアドレス範囲(addressability)を設定するのに使います。 識別子 model-name は、 smallmediumlarge のいずれかであり、 それぞれのコード・モデルを表します。 スモール・モデルのオブジェクトは (そのアドレスが ld24 命令によってロードできるよう) メモリの低位 16MB の範囲内に存在します。 ミディアム・モデルのオブジェクトとラージ・モデルのオブジェクトは、 32 ビットのアドレス空間の任意の位置に存在することができます (そのアドレスをロードするために、 コンパイラは seth/add3 命令を生成します)。

複数の属性を指定するには、 例えば `__attribute__ ((aligned (16), packed))' のように、 2重の丸括弧 (()) の中で属性をカンマで区切ります。

型属性の指定

キーワード __attribute__ により、 struct 型と union 型を定義する際に、 それらの型に特殊な属性を指定することができます。 このキーワードの後ろに、 2重の丸括弧 (()) に囲まれた属性指定が続きます。 現在、 3個の属性 alignedpackedtransparent_union が型に対して定義されています。 その他の属性が、 関数 (see section 関数属性の宣言) と変数 (see section 変数属性の指定) に対して定義されています。

キーワードの前後に `__' を付けて属性を指定することもできます。 これにより、 同じ名前を持つマクロが定義済みであるような事態を心配することなく、 ヘッダ・ファイルの中でこれらの属性を使うことができるようになります。 例えば、 aligned の代わりに __aligned__ を使うことができます。

aligned 属性と transparent_union 属性は、 typedef 宣言の中か、 完結した列挙型、構造体型、共用体型の定義の終端の波括弧 } の直後のいずれかにおいて指定することができます。 また、 packed 属性は、 定義の終端の波括弧 } の後ろにおいてのみ指定することができます。

aligned (alignment)
この属性は、 指定された型の変数の最小の境界整列の値を (バイト単位で) 指定します。 例えば、
struct S { short f[3]; } __attribute__ ((aligned (8)));
typedef int more_aligned_int __attribute__ ((aligned (8)));
という宣言によりコンパイラは、 (それが可能である限り) struct S もしくは more_aligned_int という型を持つ変数を少なくとも 8 バイト境界に割り当てて境界整列するよう保証することを強制されます。 Sparc 上では、 すべての struct S 型の変数を 8 バイト境界に境界整列させると、 複数の struct S 型の変数の間で値をコピーする際に、 コンパイラは ldd 命令と std 命令 (ダブル・ワードのロード命令とストア命令) を使うことができるので、 実行時の効率が向上します。 ANSI C 標準では、 ある与えられた struct 型もしくは union 型の境界整列の値は、 少なくとも、 その struct もしくは union のすべてのメンバの境界整列の値の最小公倍数の完全倍数になっていなければならないという点に注意してください。 このことは、 struct もしくは union の任意の1つのメンバに aligned 属性を指定することによって、 struct もしくは union の境界整列を有効に調整することが可能であるということを意味しています。 しかし、 上の例において示される表記法は、 struct 型全体もしくは union 型全体の境界整列を調整するようコンパイラに要求する方法としは、 より明白であり、 より直観的であり、 かつ、 より読みやすいものです。 前の例に示されるように、 ある与えられた struct 型もしくは union 型に対してコンパイラに使ってもらいたい境界整列の値を (バイト単位で) 明示的に指定することができます。 あるいは、 境界整列の係数(factor)を省略して、 コンパイルのターゲット・マシンにおいて有意味な最大の境界整列の値によって、 ある型を境界整列するようコンパイラに要求することもできます。 例えば、 以下のように書くことができます。
struct S { short f[3]; } __attribute__ ((aligned));
aligned 属性の指定において境界整列の係数を省略した場合にはいつでも、 コンパイルのターゲット・マシン上において任意のデータ型に対して使用される境界整列の最大値を、 その型の境界整列の値として、 コンパイラが自動的に設定します。 このようにするとしばしば、 コピー操作がより効率的になります。 というのは、 このようにして境界整列された型を持つ変数との間でコピー処理を実行する場合に、 一度にコピーできるメモリ上のデータ量が最大の命令をコンパイラが選択して使うことができるからです。 前の例では、 個々の short のサイズが 2 バイトであれば、 struct S 型全体のサイズは 6 バイトです。 この全体のサイズ以上の値を持つ2の累乗のうち最小の値は 8 です。 したがって、 コンパイラは struct S 型全体の境界整列を 8 バイトに設定します。 ある与えられた型に対して時間的に効率的な境界整列をコンパイラが選択してくれることを頼りにして、 その型のオブジェクトを個々に独立させて宣言するだけにすることもできますが、 時間的に効率的な境界整列を選択するというコンパイラの能力が主に役に立つのは、 その(効率的に境界整列された)型を持つ変数の配列を作成しようとしている場合のみであるということに注意してください。 効率的に境界整列された型を持つ変数の配列を宣言もしくは使用するプログラムでは、 その型へのポインタに対するポインタ算術 (もしくは、 それと結果的に同様のことになる添字付け) も行っている可能性があります。 こうしたポインタ算術演算に対してコンパイラが生成するコードは、 効率的に境界整列された型に対してのほうが、 そうではない型に対してよりもずっと効率的であることが多いでしょう。 aligned 属性は、 境界整列の値を大きくすることしかできません。 しかし、 packed 属性を指定することによって、 その値を小さくすることも可能です。 これについては、後に説明します。 リンカに固有の制限により、 aligned 属性の効果が制限されるかもしれないことに注意してください。 多くのシステムにおいて、 リンカは、 ある特定の境界整列の値を上限として、 その範囲内においてしか変数の境界整列を行うことができません (リンカによっては、 サポートされる境界整列の最大値が非常に小さいものもあります)。 使っているリンカが、 最大でも 8 バイト境界までしか変数の境界整列を行えないのであれば、 __attribute__ において aligned(16) を指定しても、 実際には 8 バイト境界までしか実現できません。 より詳しい情報については、 リンカのドキュメントを参照してください。
packed
enumstructunion の型定義に指定されるこの属性は、 その型を表すのに最小限必要とされるメモリだけを使うよう指定します。 struct 型と union 型に対してこの属性を指定するのは、 構造体もしくは共用体の個々のメンバに packed 属性を指定するのと同等です。 また、 コマンドライン上において `-fshort-enums' フラグを指定するのは、 すべての enum 定義において packed 属性を指定するのと同等です。 この属性を enum 定義に対して指定するのは、 enum 定義の終端の波括弧 } の後ろでしかできません。 typedef 宣言の中では、 その宣言が enum の定義を含んでいるのでない限り、 この属性を指定することはできません。
transparent_union
union の型定義に指定されるこの属性は、 任意の関数パラメータがその共用体の型を持つと、 その関数の呼び出しが特殊な方法で扱われるようになることを示します。 第1に、 transparent_union 属性が指定された共用体型に対応する引数は、 その共用体の任意のメンバの型を持つことができ、 それらの型へのキャストは一切必要ありません。 また、 その共用体のメンバにポインタ型のものがあれば、 対応する引数にはヌル・ポインタ定数もしくは void ポインタ式を使うことができます。 さらに、 その共用体のメンバに void ポインタ型のものがあれば、 対応する引数には任意のポインタ式を使うことができます。 共用体メンバの型がポインタであれば、 そのポインタによって参照される型に対する const のような修飾子は、 通常のポインタ変換の場合と同様、 尊重されなければなりません。 第2に、 関数に対してその引数が渡される際には、 transparent_union 属性が指定された共用体自体の呼び出し規約ではなく、 その共用体の1番目のメンバの呼び出し規約が使われます。 その共用体のすべてのメンバは、 同一のマシン表現(machine representation)を持っていなければなりません。 これは、 このような引数渡しが正常に機能するために必要です。 transparent_union 属性は、 互換性のために複数のインターフェイスを持つライブラリ関数のために設計されています。 例えば、 wait 関数が、 Posix との互換のためには int * 型の値を受け付けなければならず、 4.1BSD インターフェイスとの互換のためには union wait * 型の値を受け付けなければならないとしましょう。 もし wait のパラメータが void * 型であったとすると、 wait は両方の種類の引数を受け付けるでしょうが、 その他の任意のポインタ型も受け付けることになってしまい、 引数の型チェックがあまり役に立たなくなるでしょう。 こうする代わりに、 <sys/wait.h> では、 そのインターフェイスを例えば次のように定義することができます。
typedef union
  {
    int *__ip;
    union wait *__up;
  } wait_status_ptr_t __attribute__ ((__transparent_union__));

pid_t wait (wait_status_ptr_t);
このインターフェイスでは、 int * の呼び出し規約を使って、 int * 型もしくは union wait * 型の引数を渡すことができます。 プログラムは、 どちらの型の引数を使っても wait を呼び出すことができます。
int w1 () { int w; return wait (&w); }
int w2 () { union wait w; return wait (&w); }
このインターフェイスでは、 wait の実装は例えば次のようになります。
pid_t wait (wait_status_ptr_t p)
{
  return waitpid (-1, p.__ip, 0);
}
unused
unionstruct を含む) 型に対して指定されると、 この属性は、 その型の変数が存在しても、 それはおそらくは使われないはずであるということを意味します。 その型を持つ変数が何も行わないように見えても、 GNU CC は警告メッセージを出力しません。 ロックやスレッドのクラスでこのようなことがよくあります。 これらのクラスは通常、 定義されたあと参照されませんが、 これらのクラスの持つ生成関数や消去関数の中に重要な管理機能が含まれているのです。

複数の属性を指定するには、 例えば `__attribute__ ((aligned (16), packed))' のように、 2重の丸括弧 (()) の中で属性をカンマで区切ります。

マクロと同程度に高速なインライン関数

関数を inline 宣言することにより、 GNU CC にその関数のコードを呼び出し側のコードの中に組み込ませることができます。 これにより関数呼び出しのオーバーヘッドがなくなるので、 実行速度がより速くなります。 さらに、 実際の引数の値のいずれかが定数であれば、 値が既知であることを利用してコンパイル時に簡単化を行うことができるかもしれません。 この場合、 インライン関数のすべてのコードが組み込まれる必要がなくなります。 バイナリ・コードのサイズに対する影響については、 これほどはっきりと予測することができません。 インライン関数を使った場合のオブジェクト・コードは、 特定の状況に応じて、 より大きくも、 より小さくもなる可能性があります。 関数のインライン展開は最適化の1つであり、 実際には最適化コンパイルを行う場合にのみ「機能する」ものです。 `-O' を指定しなければ、関数は実際にはまったくインライン展開されません。

関数のインライン宣言を行うには、 その宣言の中で次のように inline キーワードを使います。

inline int
inc (int *a)
{
  (*a)++;
}

(ANSI C のプログラムによって組み込まれるヘッダ・ファイルを書いている場合は、 inline ではなく __inline__ と書いてください。 See section 代替キーワード)。

また、 オプション `-finline-functions' を使うことで、 すべての「十分に単純な」関数をインライン展開することもできます。 関数定義の特定の使い方によっては、 関数はインライン代替に適さないものになってしまうという点に注意してください。

C と Objective C では、 C++ とは異なり、 inline キーワードは関数の結合に影響を与えません。

GNU CC では、 C++ プログラムのクラス本体の中で定義されたメンバ関数は、 明示的に inline 宣言されていなくても、 自動的にインライン展開されます (これは `-fno-default-inline' によって無効にすることができます。 see section C++ の方言を制御するオプション)。

ある関数がインライン関数であり、 かつ、 static である場合、 その関数の呼び出されているところすべてにおいてコードが呼び出し側の中に組み込まれ、 その関数のアドレスが決して使われないのであれば、 その関数自体のアセンブラ・コードは決して参照されることがありません。 このような場合、 GNU CC は、 オプション `-fkeep-inline-functions' が指定されない限り、 その関数自体のアセンブラ・コードを実際には出力しません。 関数呼び出しには、 様々な理由のために、 組み込みのできないものがいくつかあります (特に、 関数の定義に先立つ関数呼び出しを組み込むことはできませんし、 定義中の再帰呼び出しも組み込むことはできません)。 組み込まれない関数呼び出しがあれば、 その関数は通常どおりアセンブラ・コードにコンパイルされます。 プログラムが関数のアドレスを参照する場合も、 その関数をインライン展開することはできないので、 関数は通常どおりコンパイルされなければなりません。

インライン関数が static ではない場合、 他のソース・ファイルからその関数が呼び出されるかもしれないということを、 コンパイラは想定しなければなりません。 どのようなプログラムでも、 広域シンボルはただ1度だけしか定義できないので、 その関数は他のソース・ファイルの中で定義されてはならず、 したがって、 他のソース・ファイルの中でその関数呼び出しを組み込むことはできません。 したがって、 static ではないインライン関数は、 常に通常どおりの方法で独立してコンパイルされます。

関数定義において inlineextern をともに指定すると、 その定義はインライン展開にのみ使われます。 いかなる場合でも、 その関数が独立してコンパイルされることはありません。 その関数のアドレスを明示的に参照した場合でもそうです。 そのようなアドレスは、 あたかもその関数の宣言だけが行われ、 定義は行われなかったかのように、 外部参照となります。

この inlineextern の組み合わせは、 マクロとほぼ同様の効果を持ちます。 その使い方は、 ヘッダ・ファイルの中でこれらのキーワードを使った関数定義を記述し、 ライブラリ・ファイルの中で同じ関数の定義を (inlineextern を使わずに)記述するというものです。 ヘッダ・ファイルの中の定義は、 その関数のほとんどの呼び出しをインライン展開させることになります。 もし関数としての何らかの用途が残っていれば、 ライブラリの中にただ1つ存在する関数の実体を参照することになります。

GNU C は、 最適化を行わない時には関数のインライン展開は行いません。 このような場合にインライン展開を行うのと行わないのとではどちらが良いのか明白ではありません。 しかし、 最適化を行わない場合の正しいインライン展開の実装というものは難しいということが分かったので、 簡単な方法を選んで、 インライン展開をしないことにしました。

C の式をオペランドとして持つアセンブラ命令

asm を使ったアセンブラ命令において、 その命令のオペランドを C の式を使って指定することができます。 このことは、 使いたいデータがどのレジスタもしくはメモリ位置に保持されるのかを推定する必要がないということを意味しています。

マシン記述(machine description)の中で使われるものとよく似たアセンブラ命令テンプレートに加えて、 個々のオペランドのオペランド拘束文字列(operand constraint string)を指定しなければなりません。

例として、 以下に 68881 の fsinx 命令の使い方を示します。

asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));

ここで、 angle は入力オペランドに対応する C の式であり、 result は出力オペランドに対応する C の式です。 どちらもオペランド拘束(operand constraint)として `"f"' が指定されていますが、 これは浮動小数点レジスタが必要であるということです。 `=f' の中の `=' は、 そのオペランドが出力であることを示しています。 すべての出力オペランドのオペランド拘束には、 `=' を使わなければなりません。 オペランド拘束には、 マシン記述に使われるのと同じ言語が使われます (@xref{Constraints)。

個々のオペランドは、 オペランド拘束文字列とその後ろに続く丸括弧 () に囲まれた C の式によって記述されます。 アセンブラのテンプレートと最初の出力オペランドとの間はコロンによって区切られます。 また、 入力オペランドがある場合には、 最後の出力オペランドと最初の入力オペランドとの間もコロンによって区切られます。 それぞれのオペランド・グループの中では、 カンマが個々のオペランドを区切ります。 オペランドの総数は、 10とマシン記述における任意の命令パターンが持つオペランドの最大数とのうち大きいほうが上限となります。

出力パラメータがなくて入力オペランドがある場合には、 本来出力オペランドが置かれるべき箇所のまわりに2つのコロンを連続して記述しなければなりません。

出力オペランド式はロケータ値(lvalue)でなければなりません。 コンパイラはこれをチェックすることができます。 入力オペランドはロケータ値でなくても構いません。 オペランドのデータ型が、 実行されている命令に対して適切であるか否かを、 コンパイラはチェックすることができません。 コンパイラはアセンブラ命令テンプレートを解析しませんし、 それが意味するところも、 そもそもそれが正当なアセンブラ命令であるのかどうかということすらコンパイラには分かりません。 拡張された asm 機能は、 コンパイラがそもそもそういう命令が存在することすら知らないようなマシン命令に対して、 非常にしばしば使われます。 出力式のアドレスを直接取ることができない場合 (例えば、ビット・フィールドである場合)、 オペランド拘束によってレジスタを使えるようにしなければなりません。 そのようになっていれば、 GNU CC は、 そのレジスタを asm の出力として使い、 そのあとで、 そのレジスタの内容を出力オペランドへ格納します。

通常の出力オペランドは書き込みのみ可能でなければなりません。 GNU CC は、 命令が実行される前にこれらのオペランドに入っている値は無意味であり、 したがって、 その値を生成する必要はないものと想定します。 拡張された asm は、 入出力オペランド、 すなわち、 読み書き可能なオペランドをサポートしています。 オペランドがこのようなものであることを示すためには、 拘束文字 `+' を使います。 入出力オペランドは、 出力オペランドと一緒に並べて記述します。

読み書き可能なオペランド (もしくは、 一部のビットのみが変更されるオペランド) に対するオペランド拘束にレジスタを指定することができれば、 そのようなオペランドの代わりに、 1つの入力オペランドと1つの書き込みのみ可能な出力オペランドの2つの別々のオペランドを使って、 その機能を論理的に分割することができます。 これら2つのオペランドの間の関連は、 その命令を実行する際にその2つのオペランドは同じ箇所に存在しなければならないと指定するオペランド拘束によって表現することができます。 両方のオペランドに対して同じ C の式を使うこともできますし、 異なる式を使うこともできます。 例えば以下では、 bar を読み込みのみ可能なソース・オペランド、 foo を読み書き可能な目的(destination)オペランドとして、 (架空の) `combine' 命令を記述しています。

asm ("combine %2,%0" : "=r" (foo) : "0" (foo), "g" (bar));

オペランド 1 のオペランド拘束 `"0"' は、 それがオペランド 0 と同じ箇所に存在しなければならないということを指定しています。 オペランド拘束の中で数字を使うのは入力オペランドの中でのみ許され、 それは出力オペランドを参照するものでなければなりません。

オペランド拘束の中の数字だけが、 あるオペランドが別のオペランドと同じ箇所に存在することを保証することができます。 単に foo が両方のオペランドの値であるということだけでは、 生成されるアセンブラ・コードの中でそれらのオペランドが同じ箇所に存在することを保証するのに十分ではありません。 以下のような記述では、 その動作はあてになりません。

asm ("combine %2,%0" : "=r" (foo) : "r" (foo), "g" (bar));

様々な最適化や再ローディング(reloading)によって、 オペランド 0 とオペランド 1 とが別のレジスタに置かれることがありえます。 そうしてはならない理由を GNU CC は知りません。 例えば、 コンパイラは、 foo の値のコピーをあるレジスタの中に見つけて、 これをオペランド 1 に対して使う一方で、 出力オペランド 0 は別のレジスタに生成する (後になって、 そのレジスタの内容を foo 自体のアドレスにコピーする) かもしれません。 オペランド 1 に対応するレジスタはアセンブラ・コードの中で言及すらされていないので、 結果は当然うまくいかないことになりますが、 GNU CC にはそのことを知る由もありません。

命令の中には、 特定のハード・レジスタの内容を破壊するものがあります。 このことを記述するには、 入力オペランドの後ろに3番目のコロンを書き、 その後ろに内容の破壊されるハード・レジスタの名前を (文字列として) 書きます。 以下に、 VAX における実際の例を示します。

asm volatile ("movc3 %0,%1,%2"
              : /* 出力オペランドを使わない */
              : "g" (from), "g" (to), "g" (count)
              : "r0", "r1", "r2", "r3", "r4", "r5");

アセンブラ・コードから特定のハードウェア・レジスタを参照するのであれば、 それらのレジスタの値が変更されるということをコンパイラに通知するために、 3番目のコロンの後ろにそれらのレジスタをおそらく指定しなければならなくなるでしょう。 アセンブラによっては、 レジスタの名前が `%' で始まるものもいくつかあります。 アセンブラ・コードの中に `%' を1つ生成するためには、 元になる入力の中では `%%' と書かなければなりません。

アセンブラ命令が条件コード・レジスタ(condition code register)の内容を変更する可能性があれば、 破壊されるレジスタのリストの中に `cc' を加えてください。 いくつかのマシンにおいて GNU CC は、 条件コード(condition code)を特定のハードウェア・レジスタとして表します。 `cc' はこのレジスタの名前として使えます。 その他のマシン上では、 条件コードは異なる取り扱いを受けるため、 `cc' を指定しても何の効果もありません。 しかし、 どちらの種類のマシンにおいても、 `cc' を指定しても誤りにはなりません。

アセンブラ命令が予測不可能な形でメモリ上の内容を変更するのであれば、 破壊されるレジスタのリストに `memory' を加えてください。 これにより GNU CC は、 そのアセンブラ命令のところでは、 メモリ上の値をレジスタの中にキャッシュしたまま保持することはしなくなります。

個々のアセンブラ命令を (`\n' として書かれる) 改行によって区切るか、 もしくは、 アセンブラがセミコロンの使用を許すのであればセミコロンによって区切ることにより、 1つの asm テンプレートの中に複数のアセンブラ命令を一緒にして入れることができます。 GNU アセンブラではセミコロンを使うことができますし、 ほとんどの Unix 上のアセンブラも同様のようです。 入力オペランドには、 内容が破壊されると指定されたレジスタは一切使われないことが保証されています。 出力オペランドのアドレスについても同様のことが保証されています。 したがって、 内容が破壊されると指定されたレジスタは好きなだけ読み書きすることができます。 以下に、 1つのテンプレートの中に複数の命令を記述した例を示します。 この例は、 サブルーチン _foo が、 レジスタ 9 とレジスタ 10 から引数を受け取ることを想定しています。

asm ("movl %0,r9;movl %1,r10;call _foo"
     : /* 出力オペランドを使わない */
     : "g" (from), "g" (to)
     : "r9", "r10");

出力オペランドに拘束変換修飾子(constraint modifier)`&' が指定されていなければ、 GNU CC は、 出力が生成される時には既に入力は使い終わっているという想定のもとに、 出力オペランドとは無関係な入力オペランドが使っているのと同じレジスタに出力オペランドを割り当てるかもしれません。 アセンブラ・コードが実際には複数の命令から構成されている場合、 この想定は正しくない可能性があります。 そのような場合には、 入力オペランドと同じ箇所に割り当てられてはならない出力オペランド1つ1つに対して `&' を使ってください。 @xref{Modifiers。

アセンブラ命令によって生成される条件コードをテストしたいのであれば、 以下のように、 分岐命令(branch)と分岐先ラベルを asm の中に入れなければなりません。

asm ("clr %0;frob %1;beq 0f;mov #1,%0;0:"
     : "g" (result)
     : "g" (input));

ここでは、 GNU アセンブラとほとんどの Unix 上のアセンブラがそうであるように、 アセンブラが局所ラベルをサポートしていることを想定しています。

ラベルに関しては、 1つの asm から別の asm へのジャンプはサポートされていません。 コンパイラの最適化処理部はこのようなジャンプのことを知りません。 したがって、 どのように最適化を行うかを決定する際に、 このようなジャンプのことを考慮に入れることができません。

通常、 これらの asm 命令を使うのに最も便利な方法は、 関数のように見えるマクロの中に入れてしまうことです。 以下に例を示します。

#define sin(x)       \
({ double __value, __arg = (x);   \
   asm ("fsinx %1,%0": "=f" (__value): "f" (__arg));  \
   __value; })

ここでは、 適切な double 型の値に対して命令が実行されるのを確実にするために、 また、 自動的に double 型に変換できるような引数 x のみを受け取るようにするために、 変数 __arg が使われています。

正しいデータ型に対して命令が実行されるのを確実にするための別の方法として、 asm の中でキャストを使う方法があります。 これは、 変数 __arg を使う方法とは、 違いのより大きい型からでも変換を行うという点が異なります。 例えば、 要求されている型が int である場合、 引数を int 型へキャストしてやれば、 その引数がポインタであっても文句なく受け付けてくれるでしょうが、 引数を __arg という名前の int 型の変数に代入しようとすると、 呼び出し側で明示的にキャストが行われていない限り、 ポインタを使っていることに関して警告メッセージが出力されるでしょう。

asm が出力オペランドを持つ場合、 GNU CC は最適化を行うために、 その命令には出力オペランドを変更すること以外に副作用はないと想定します。 このことは、 副作用を持つ命令を使うことはできないということを意味する訳ではありませんが、 出力オペランドが使われないと、 コンパイラがそれらの命令を除去したり、 ループの外に移動したり、 あるいは、 2つの命令が共通する部分式を構成する場合には、 2つの命令を1つに置き換えたりしますので、 注意しなければなりません。 また、 その副作用さえなければ変更されるようには見えないある変数に対して命令が副作用を持つ場合、 その変数の古い値がたまたまレジスタの中にあると、 その古い値が後に再度使用されるかもしれません。

asm の後ろにキーワード volatile を書くことによって、 asm 命令が除去されたり、 大きく移動されたり、 1つにまとめられたりすることを防ぐことができます。 以下に例を示します。

#define get_and_set_priority(new)  \
({ int __old; \
   asm volatile ("get_and_set_priority %0, %1": "=g" (__old) : "g" (new)); \
   __old; })

出力オペランドを持たない asm 命令を書くと、 GNU CC は、 その命令が副作用を持つものと理解し、 その命令の除去やループの外への移動を行いません。 命令の持つ副作用が純粋に外部的なものではなく、 入力の読み込みや指定されたレジスタもしくはメモリの内容の破壊とは異なる方法でプログラムの中の変数に影響を及ぼすのであれば、 将来のバージョンの GNU CC がコア領域(core region)の内部で命令を移動するようになるのを予防するために、 volatile キーワードを指定するべきでしょう。

オペランドや破壊されるレジスタを一切持たない asm 命令 (および「古い方式」の asm) は、 volatile キーワードを指定した場合と同様に、 削除されることもありませんし、 到達不可能でない限り、 大きく移動されることもありません。

volatile 指定された asm 命令であっても、 交差する複数のジャンプ命令などは、 コンパイラにとっては無意味に見えるような方法で移動することができます。 volatile 指定された asm 命令の順序が、 完全に連続的なままに維持されると期待することはできません。 命令を連続的に出力したいのであれば、 単一の asm を使ってください。

アセンブラ命令が残した条件コードにアクセスするための方法を探そうと考えるのは自然なことです。 しかし、 このような方法を実装しようと試みた結果、 これを確実に実行する方法がないことが分かりました。 問題になるのは、 出力オペランドが再ローディング(reloading)を必要とするかもしれないという点です。 これにより、 追加的な「ストア」命令が実行されることになります。 ほとんどのマシン上では、 この追加的な命令によって、 条件コードをテストする時間のないまま条件コードが変更されることになってしまいます。 この問題は、 通常の「テスト」命令と「比較」命令では発生しません。 その理由は、 これらの命令が出力オペランドを持たないからです。

ANSI C プログラムによって組み込まれなければならないヘッダ・ファイルを書いている場合には、 asm ではなく __asm__ と書いてください。 See section 代替キーワード

アセンブラ・コードの中で使われる名前の制御

C の関数もしくは変数に対してアセンブラ・コードの中で使われる名前を、 以下のように、 その宣言子の後ろに asm (もしくは __asm__) キーワードを書くことで指定することができます。

int foo asm ("myfoo") = 2;

これは、 変数 foo に対してアセンブラ・コードの中で使われる名前が、 通常の `_foo' ではなく、 `myfoo' であると指定するものです。

C の関数もしくは変数の名前の前に通常はアンダースコアが付加されるシステム上においては、 この機能によって、 リンカのためにアンダースコアで始まらない名前を定義することができます。

関数の定義において、 このような方法で asm を使うことはできません。 しかし以下のように、 関数の宣言をその定義の前に書き、 そこに asm を置くことによって、 同等の効果を得ることができます。

extern func () asm ("FUNC");

func (x, y)
     int x, y;
...

選択されたアセンブラでの名前が他のどのアセンブラのシンボルとも確実に一致しないようにするのは、 その名前を選択した人の責任です。 また、 レジスタの名前を使ってはなりません。 レジスタの名前を使うと、 まったく不当なアセンブラ・コードが生成されることになります。 GNU CC では現在のところ、 静的変数をレジスタの中に格納することはできません。 おそらくいつか、 このようなこともできるようになるでしょう。

指定されたレジスタの中の変数

GNU C では、 少数の広域変数を指定されたハードウェア・レジスタの中に置くことができます。 また、 通常のレジスタ変数が割り当てられるべきレジスタを指定することもできます。

広域レジスタ変数の定義

GNU C では以下のようにして、 広域レジスタ変数を定義することができます。

register int *foo asm ("a5");

ここで、 a5 は使われるべきレジスタの名前です。 ライブラリ・ルーチンがそのレジスタの内容を破壊しないように、 マシン上の通常の関数呼び出しによって待避と復元が行われるレジスタを選択してください。

レジスタの名前は当然 CPU に依存しますので、 CPU の種類に応じてプログラムを条件付けする必要があります。 レジスタ a5 は、 68000 上でポインタ型の変数に対して選択すると良いでしょう。 レジスタ・ウィンドウを持つマシン上では、 関数呼び出しのメカニズムによって不思議な影響を受けることのない「広域」レジスタを必ず選択するようにしてください。

さらに、 同一種類の CPU 上でも、 オペレーティング・システムが異なるとレジスタの名前の付け方が異なるかもしれません。 このような場合には、 さらに追加の条件付けが必要になります。 例えば、 68000 上のオペレーティング・システムには、 先ほどのレジスタを %a5 と呼ぶものがいくつかあります。

最終的には、 レジスタを自動的に選択するようコンパイラに要求する方法が実現できるかもしれませんが、 それにはまず最初に、 コンパイラはどうやってレジスタを選択するべきか、 また、 コンパイラの選択についてユーザが指針を与えることができるようにするにはどうすればよいか、 という点を解決する必要があります。 明白な解決策はありません。

特定のレジスタにおいて広域レジスタ変数を定義すると、 少なくともカレントなコンパイル(current compilation)の中においては、 そのレジスタは完全にこの用途のために確保されてしまいます。 そのレジスタは、 カレントなコンパイルの中では、 関数の中でその他のどのような目的のためにも割り当てられることがありません。 そのレジスタは、 関数によって待避も復元もされません。 このレジスタの中に格納された値は、 たとえその値が無効になったように見えても、 決して削除されることはありませんが、 その値に対する参照は、 削除、 移動、 単純化が行われることがあるかもしれません。

シグナル・ハンドラの中や複数の制御スレッドの中から広域レジスタ変数にアクセスするのは安全ではありません。 これは、 (実行したいタスクのためにシステム・ライブラリのルーチンを特に再コンパイルするのでない限り) システム・ライブラリのルーチンが一時的にそのレジスタを他の用途で使うかもしれないからです。

広域レジスタ変数を使うある関数から、 その変数に関する情報を参照せずにコンパイルされた (すなわち、 その変数が宣言されていない別のソース・ファイルの中にある) 第3の関数 lose を経由して、 最初の関数と同様に広域レジスタ変数を使う別の関数 foo を呼び出すことは安全ではありません。 これは、 lose がそのレジスタを待避して、 何か別の値をそこに入れるかもしれないからです。 例えば、 qsort に渡す比較関数の中において広域レジスタ変数が利用可能であると期待することはできません。 というのは、 qsort がそのレジスタに何か別の値を入れてしまうかもしれないからです (この広域レジスタ変数を組み込んで qsort を再コンパイルする用意があれば、 この問題を解決することができます)。

qsort やその他のソース・ファイルが、 ある広域レジスタ変数を他のどのような目的にも使うことがないように、 実際にはその広域レジスタ変数を使うことのないそれらのソース・ファイルを再コンパイルしたいのであれば、 コンパイラ・オプション `-ffixed-reg' を指定するだけで十分です。 実際にそれらのソース・コードに広域レジスタ宣言を加える必要はありません。

広域レジスタ変数の値を変更する可能性のある関数は、 その関数からの復帰時に呼び出し側がそのレジスタの中に見つけることができるものと期待している値を破壊するかもしれないため、 その広域レジスタ変数に関する情報を参照せずにコンパイルされた関数から安全に呼び出すことはできません。 したがって、 広域レジスタ変数を使うプログラム部分への入口になっている関数は、 呼び出し側に属する値を明示的に待避および復元しなければなりません。

ほとんどのマシン上において、 longjmp は、 setjmp が呼び出された時点において個々の広域レジスタ変数が持っていた値によって、 それらのレジスタ変数を復元します。 しかしマシンによっては、 longjmp が広域レジスタ変数の値を変更しないものもいくつかあります。 移植性があるようにするためには、 setjmp を呼び出した関数が追加的な調整作業を実行することにより、 広域レジスタ変数の値が待避され、 かつ、 longjmp の中でその値が復元されるようにするべきです。 こうすることによって、 longjmp の処理内容にかかわらず、 同一の結果がもたらされることになります。

すべての広域レジスタ変数の宣言は、 すべての関数定義に先立って行われなければなりません。 広域レジスタ変数の宣言が関数定義の後ろにあると、 その宣言よりも前にある関数によってそのレジスタが他の目的で使われてしまうのを防ぐのに手遅れになってしまいます。

実行ファイルからレジスタに対して初期値を提供するための手段は存在しないので、 広域レジスタ変数は初期値を持つことができません。

Sparc 上では、 g3 ... g7 が適切なレジスタであるという報告がされていますが、 商と剰余を求めるサブルーチンのほかに、 getwd のような特定のライブラリ関数が、 g3 と g4 の値を変更します。 g1 と g2 は一時的な用途で使われる局所的なレジスタです。

68000 上では、 a2 ... a5 および d2 ... d7 が適切なはずです。 もちろん、 これらのうち実際に使うのは少数にとどめるべきです。

局所変数に対するレジスタの指定

以下のようにして、 指定したレジスタによって局所レジスタ変数を定義することができます。

register int *foo asm ("a5");

ここで、 a5 は使われるべきレジスタの名前です。 これは広域レジスタ変数を定義するのに使った構文と同一ですが、 局所変数の場合、 定義は関数の内部に置かれるという点に注意してください。

レジスタの名前は当然 CPU に依存しますが、 特定のレジスタを指定することは、 ほとんどの場合、 明示的なアセンブラ命令(see section C の式をオペランドとして持つアセンブラ命令)において役に立つものですので、 このような依存性は問題になりません。 こうした事情により通常は、 CPU の種類に応じてプログラムを条件付けする必要があります。

さらに、 同一種類の CPU 上でも、 オペレーティング・システムが異なるとレジスタの名前の付け方が異なるかもしれません。 このような場合には、 さらに追加の条件付けが必要になります。 例えば、 68000 上のオペレーティング・システムには、 先ほどのレジスタを %a5 と呼ぶものがいくつかあります。

このようなレジスタ変数を定義しても、 レジスタは確保されません。 フロー制御処理部がその変数の値が無効であると決定した箇所では、 そのレジスタは他の用途に利用することができます。 しかし、 再ロード(reload)のパスにおいては、 これらのレジスタは利用できなくなります。 この機能を過度に使いすぎると、 特定の関数をコンパイルする際にコンパイラが利用できるレジスタの数が非常に少なくなってしまいます。

このオプションを使っても、 この変数が常に指定されたレジスタの中にあるようなコードを GNU CC が生成してくれることが保証される訳ではありません。 asm 文の中で明示的にこのレジスタを参照するようなコーディングをして、 そのレジスタは常にこの変数を参照していると想定してはいけません。

代替キーワード

オプション `-traditional' を使うと、 特定のキーワードが利用できなくなります。 また、 オプション `-ansi' を使うと、 別の特定のキーワードが利用できなくなります。 ANSI C のプログラムや伝統的な C のプログラムも含むすべてのプログラムにおいて利用可能でなければならない汎用的なヘッダ・ファイルの中で、 GNU C の拡張機能や ANSI C の機能を使いたい場合に、 これが問題になります。 キーワード asmtypeofinline は、 `-ansi' を指定してコンパイルされるプログラムの中では問題があるので使うことができません。 その一方で、 キーワード constvolatilesignedtypeofinline は、 `-traditional' を指定してコンパイルされるプログラムの中では問題があります。

この問題を解決する方法は、 問題のある個々のキーワードの前後に `__' を付けることです。 例えば、 asm の代わりに __asm__ を、 const の代わりに __const__ を、 inline の代わりに __inline__ を使ってください。

他の C コンパイラは、 このような代替キーワードを受け付けてくれません。 別のコンパイラでコンパイルを行いたいのであれば、 代替キーワードをマクロとして定義して、 慣習的なキーワードに置き換えることができます。 これは、 以下のようになります。

#ifndef __GNUC__
#define __asm__ asm
#endif

`-pedantic' を指定すると、 多くの GNU C 拡張機能に対して警告メッセージが出力されます。 式の前に __extension__ と書くことによって、 1つの式の中でこのような警告メッセージの出力を防ぐことができます。 __extension__ にはこれ以外の作用はありません。

不完全な enum

enum タグを、 それが取ることのできる値を指定しないまま、 定義することができます。 これは、 struct foo と書いてその要素を記述しないようなもので、 不完全な型になります。 後に、 それが取ることのできる値を指定する宣言をすることによって、 その型は完全なものになります。

この型が不完全な間は、 それを使って変数や記憶域を割り当てることはできません。 しかし、 その型へのポインタを操作することはできます。

この拡張機能はあまり役に立たないかもしれませんが、 これによって enum の取り扱いが、 structunion を取り扱う方法とさらに首尾一貫したものになります。

この拡張機能は GNU C++ ではサポートされていません。

文字列としての関数名

GNU CC では、 カレントな関数の名前を値として持つ2つの文字列変数があらかじめ定義されています。 変数 __FUNCTION__ は、 ソース・コードの中に記述されたとおりの関数名です。 一方、 変数 __PRETTY_FUNCTION__ は、 言語に固有の流儀できれいに出力された(pretty printed)関数名です。

この2つの名前は C の関数では常に同一ですが、 C++ の関数の場合は異なることがあります。 例えば、

extern "C" {
extern int printf (char *, ...);
}

class a {
 public:
  sub (int i)
    {
      printf ("__FUNCTION__ = %s\n", __FUNCTION__);
      printf ("__PRETTY_FUNCTION__ = %s\n", __PRETTY_FUNCTION__);
    }
};

int
main (void)
{
  a ax;
  ax.sub (0);
  return 0;
}

というプログラムを実行すると、 次のような出力が得られます。

__FUNCTION__ = sub
__PRETTY_FUNCTION__ = int  a::sub (int)

これらの名前はマクロではなく、 あらかじめ定義された文字列変数です。 例えば、 `#ifdef __FUNCTION__' は関数の中では特別な意味を持ちません。 これは、 識別子 __FUNCTION__ について、 プリプロセッサが何も特別なことを行わないからです。

関数の復帰アドレスとフレーム・アドレスの獲得

ある関数の呼び出し側に関する情報を入手するために以下の関数を使うことができます。

__builtin_return_address (level)
この関数は、 カレントな関数の復帰アドレス、 もしくは、 カレントな関数を呼び出すまでに途中で呼び出されてきた関数の中の1つの復帰アドレスを返します。 引数 level で指定された数のフレームを呼び出しスタック中でさかのぼって調べます。 値 0 を指定すると、 カレントな関数の復帰アドレスが返ってきます。 値 1 を指定すると、 カレントな関数を呼び出した関数の復帰アドレスが返ってきます。 以下、 同様です。 引数 level は整数の定数でなければなりません。 マシンによっては、 カレントな関数以外の関数の復帰アドレスを決定することが不可能なものもあります。 そのような場合、 もしくは、 スタックのトップに達してしまった場合には、 この関数は 0 を返します。 この関数をデバッグの目的で使う際には、 引数には0(ゼロ)以外の値だけを指定するべきです。
__builtin_frame_address (level)
この関数は、 __builtin_return_address に似ていますが、 関数の復帰アドレスではなく関数フレームのアドレスを返します。 値 0 を指定して __builtin_frame_address を呼び出すと、 カレントな関数のフレーム・アドレスが返ってきます。 値 1 を指定すると、 カレントな関数を呼び出した関数のフレーム・アドレスが返ってきます。 以下、 同様です。 フレームとは、 局所変数や待避されたレジスタを保持している、 スタック上の領域のことです。 通常、 フレーム・アドレスとは、 関数によって最初にスタックにプッシュされたワードのアドレスのことです。 しかし、 正確な定義は、 プロセッサと呼び出し規約とに依存します。 プロセッサが専用のフレーム・ポインタ・レジスタを持つ場合、 関数がフレームを持っていると、 __builtin_frame_address はフレーム・ポインタ・レジスタの値を返します。 __builtin_return_address にあてはまる注意事項は、 この関数にもあてはまります。


[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]