【GNU笔记】【C扩展系列】标签作为值 Labels as Values
标签作为值 Labels as Values
你可以使用一元运算符“&&”获取当前函数(或包含函数)中定义的标签的地址。值的类型为void *
。这个值是一个常量,可以在该类型的常量有效的任何地方使用。例如:
void *ptr;
/* … */
ptr = &&foo;
要使用这些值,你需要能够跳转到一个值。这可以通过计算goto(computed goto)语句1,goto *exp;
来实现。比如说。
goto *ptr;
允许使用void*类型的任何表达式。
使用这些常量的一种方法是初始化用作跳转表的静态数组:
static void *array[] = { &&foo, &&bar, &&hack };
然后你可以选择一个带有索引的标签,像这样:
goto *array[i];
请注意,这并不检查下标是否在边界内–C语言中的数组索引从来不会这样做。
这种标签值数组的用途与switch
语句的用途非常相似。switch
语句更干净,所以使用它而不是数组,除非问题不适合switch
语句。
标签值的另一个用途是用于线程代码的解释器中。解释器函数中的标签可以存储在线程代码中,以便进行超快速的调度。
你不能使用这种机制来跳转到不同函数中的代码。如果你这样做,会发生完全不可预测的事情。避免这种情况的最好方法是只将标签地址存储在自动变量中,而绝不将其作为参数传递。
上述示例的另一种写法是
static const int array[] = { &&foo - &&foo, &&bar - &&foo,
&&hack - &&foo };
goto *(&&foo + array[i]);
这对共享库中的代码更友好,因为它减少了所需的动态重定位的数量,因此允许数据为只读。AVR 目标不支持这种带有标签差异的替代方法,请对 AVR 程序使用第一种方法。
如果包含函数是内联的(inlined)或克隆的(cloned),则同一标签的&&foo
表达式可能具有不同的值。如果一个程序依赖于它们总是相同的,那么应该使用__attribute__((__noinline__,__noclone__))
来防止内联和克隆。如果在静态变量初始值设定项中使用&&foo
,则禁止内联和克隆。
脚注
[参考资料]
Fortran 中的类似功能称为分配的 goto(assigned goto),但该名称在C语言中似乎并不合适,在C语言中,人们可以做的不仅仅是将标签地址存储在标签变量中。 ↩︎