Oracle® Solaris Studio 12.4: C ユーザーガイド

印刷ビューの終了

更新: 2014 年 12 月
 
 

2.5 値としてのラベル

C コンパイラは、計算型 goto 文として知られる C の拡張機能を認識します。計算型 goto 文を使用すると、実行時に分岐先を判別することができます。次のように演算子 '&&' を使用して、ラベルのアドレスを取得し、void * 型のポインタに割り当てることができます。

void *ptr;
...
ptr = &&label1;

あとに続く goto 文は、ptr により label1 に分岐できます。

goto *ptr;

ptr は実行時に計算されるため、ptr は有効範囲内にある任意のラベルのアドレスを取得でき、goto 文はこのアドレスに分岐することができます。

ジャンプテーブルを実装するには、計算型 goto 文を次の方法で使用します。

static void *ptrarray[] = { &&label1, &&label2, &&label3 };

これで、次のようにインデックスを指定して配列要素を選択できます。

goto *ptrarray[i];

ラベルのアドレスは、現在の関数スコープからのみ計算できます。現在の関数以外のラベルについてアドレスを取得しようとすると、予測できない結果になります。

ジャンプテーブルは switch 文と同様の働きをしますが、ジャンプテーブルではプログラムフローの追跡がより困難になる可能性があります。顕著な相違点は、switch 文のジャンプ先はすべて、予約語 switch から見て順方向になることです。計算型 goto を使用してジャンプテーブルを実装すれば、順方向、逆方向のどちらにも分岐させることができます。

#include <stdio.h>
void foo()
{
  void *ptr;

  ptr = &&label1;

  goto *ptr;

  printf("Failed!\n");
  return;

  label1:
  printf("Passed!\n");
  return;
}

int main(void)
{
  void *ptr;

  ptr = &&label1;

  goto *ptr;

  printf("Failed!\n");
  return 0;

  label1:
  foo();
  return 0;
}

次の例では、プログラムフローの制御にジャンプテーブルを使用しています。

#include <stdio.h>

int main(void)
{
  int i = 0;
  static void * ptr[3]={&&label1, &&label2, &&label3};

  goto *ptr[i];

  label1:
  printf("label1\n");
  return 0;

  label2:
  printf("label2\n");
  return 0;

  label3:
  printf("label3\n");
  return 0;
}

%example: a.out
%example: label1

計算型 goto の別の利用は、スレッド化されたコードのインタプリタとしてです。高速ディスパッチを行うために、インタプリタ関数の範囲内にあるラベルアドレスを、スレッド化されたコードに格納することができます。