GCC提供了一个扩展--取得当前函数中标号地址(labels as values),这个对于实现Threaded Code技术非常有用,这里简单介绍一下,基本上是把GCC在线手册(http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Labels-as-Values.html#Labels-as-Values) 对应处翻译了过来。
你可以使用一元操作符&&得到在当前函数(或包含函数)中定义的标签的地址,得到的值的类型是“void*”。这个值是一个常量,可以在任何使用这个类型的常量的地方使用。例如:
void *ptr;
/* ... */
ptr = &&foo;
为了使用这些标签值,我们可以使用GCC的计算Goto语句“goto *exp”(computed goto statement,在FORTRAN里面称为Assigned Goto)跳转到对应标签处。例如:
goto *ptr;
在这里任何void *类型的表达式都是合法的。
标签常量的一个常见的用处就是初始化一个静态数组,作为跳转表来使用。例如:
static void *array[] = { &&foo, &&bar, &&hack };
这样你就可以通过索引来选择一个标签,如:
goto *array[i];
注意这句语句中没有检查下标是否越界--在C语言中数组下标从来不会检查是否越界。
这里的标签数组的使用目的和switch语句十分类似。switch语句的表达要比标签数组更为清晰,所以除非遇到switch语句实在无法适用的场合才考虑使用标签数组。
标签量的另外一种用法是用在Threaded Code类型的解释器程序(interpreter)中。解释器函数中的标签可以被存储到Threaded Code中来获得极高的分发(dispatch)效率。
你不能使用这种机制来跳到另一个函数中的代码中,如果你这么做的了,结果完全不可预料。避免这种情况最好的办法就是只将标签地址存储在自动变量(Automatic variables)中并且永远不要把它作为参数传递。
上面的例子的另外一种写法是:
static const int array[] = { &&foo - &&foo, &&bar - &&foo, &&hack - &&foo };
goto *(&&foo + array[i]);
这种方式在编写共享库中代码的时候显得更为友好,因为它减少了必须的动态地址重定位的次数从而运行这些(地址)值是只读的。
这里给出一个极其简单的使用此特性所写的Threaded interperter的例子:
#define NEXT goto **ip++
#define guard(n) asm("#" #n)
int main(int argc, char *argv[])
{
/* this has 50% mispredictions (50-60% is typical in large benchmarks) */
static void *prog[] = {&&next1,&&next2,&&next1,&&next3,&&next1,&&next4,&&next1,&&next5,&&next1,&&loop};
void **ip=prog;
int count = 10000000;
NEXT;
next1: guard(1); NEXT;
next2: guard(2); NEXT;
next3: guard(3); NEXT;
next4: guard(4); NEXT;
next5: guard(5); NEXT;
loop:
if (count>0) {
count--;
ip=prog;
NEXT;
}
return 0;
}