Sun Studio 12:C 用户指南

2.5 作为值的标签

C 编译器可识别称为计算转移 (computed goto) 语句的 C 扩展。使用计算转移 (computed goto) 语句能够在运行时确定分支目标。通过使用 '&&' 运算符可以获取标签的地址,并且可以将标签地址指定给 void * 类型的指针:


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

后面的 goto 语句可以通过 ptr 转到 label1


goto *ptr;

由于 ptr 在运行时进行计算,因此 ptr 可以接受作用域内任何标签的地址,而 goto 语句可以转到该位置。

使用计算转移 (computed goto) 语句的一种方法是用于转移表的实现:


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

现在可以通过索引来选择数组元素:


goto *ptrarray[i];

标签的地址只能通过当前函数作用域计算。尝试在当前函数外部获取标签的地址会产生不可预测的结果。

转移表和开关语句的作用相似(虽然存在某些主要差异),转移表使跟踪程序流更加困难。一个显著的差异是:开关语句转移目标全都从开关保留字开始正向转移;使用计算转移 (computed 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

计算转移 (computed goto) 语句的另一个应用是作为线程代码的解释程序。解释程序函数内部的标签地址可以存储在线程代码中以便快速分发。

下面是编写以上示例的另一种方法:


static const int ptrarray[] = { &&label1 - &&label1, &&label2 - &&label1, &&label3 - &&label1 };
goto *(&&label1 + ptrarray[i]);

这对于共享库代码效率更高,因为它减少了所需的动态重定位数量,因此,允许数据(ptrarray 元素)为只读。