如何将goto标签存储在数组中然后跳转到它们?

时间:2009-06-02 08:35:10

标签: c++ c goto jump-table

我想声明一个“jumplabels”数组。

然后我想跳到这个阵列中的“jumplabel”。

但我不知道该怎么做。

它应该类似于以下代码:

function()
{
    "gotolabel" s[3];
    s[0] = s0;
    s[1] = s1;
    s[2] = s2;

    s0:
    ....
    goto s[v];

    s1:
    ....
    goto s[v];

    s2:
    ....
    goto s[v];
}

有没有人知道如何执行此操作?

11 个答案:

答案 0 :(得分:40)

GCC功能可以称为“labels as values”。

void *s[3] = {&&s0, &&s1, &&s2};

if (n >= 0 && n <=2)
    goto *s[n];

s0:
...
s1:
...
s2:
...

仅适用于GCC!

答案 1 :(得分:17)

goto需要一个编译时标签。

从这个例子看,你似乎正在实现某种状态机。最常见的是,它们被实现为switch-case结构:

while (!finished) switch (state) {
  case s0:
  /* ... */
  state = newstate;
  break;

  /* ... */
}

如果您需要它更具动态性,请使用函数指针数组。

答案 2 :(得分:12)

没有直接的方法来存储代码地址以跳转到C. 如何使用开关。

#define jump(x)  do{ label=x; goto jump_target; }while(0)
int label=START;
jump_target:
switch(label)
{
    case START:
        /* ... */
    case LABEL_A:
        /* ... */
}

您可以找到每个无堆栈解析器/状态机生成器生成的类似代码。 这样的代码不容易理解,除非它是生成代码或你的问题最多 很容易通过状态机描述我建议不要这样做。

答案 3 :(得分:8)

你可以使用函数指针而不是goto吗?

这样你就可以创建一个函数数组来调用和调用相应的函数。

答案 4 :(得分:6)

在简单的标准C中,据我所知,这是不可能的。然而,GCC编译器中有一个扩展documented here,这使得这成为可能。

扩展程序引入了新的运算符&&,以获取标签的地址,然后可以将其与goto语句一起使用。

答案 5 :(得分:5)

这就是switch陈述的用途。

switch (var)
{
case 0:
    /* ... */
    break;
case 1:
    /* ... */
    break;
default:
    /* ... */
    break;  /* not necessary here */
}

请注意,编译器不一定会将其转换为跳转表。

如果您真的想自己构建跳转表,可以使用函数指针数组。

答案 6 :(得分:3)

你可能想看看setjmp / longjmp。

答案 7 :(得分:2)

你不能用goto做 - 标签必须是标识符,而不是变量或常量。我不明白为什么你不想在这里使用一个开关 - 它可能会同样有效,如果这是关于你的。

答案 8 :(得分:2)

对于一个简单的答案,而不是强迫编译器做真正的愚蠢的事情,学习良好的编程实践。

答案 9 :(得分:1)

标记生成器?这看起来像是为什么制造了gperf。不,真的,看看它。

答案 10 :(得分:1)

优化编译器(包括GCC)会将switch语句编译成跳转表(使switch语句与您尝试构建的语句完全一样快)如果满足以下条件:

您的开关案例(州号)从零开始。

您的开关箱严格增加。

切换案例中不会跳过任何整数。

有足够多的情况下跳转表实际上更快(在检查每个案例处理switch语句的方法中,有几十个比较并且比跳转表更快。)

这样做的好处是允许您使用标准C编写代码,而不是依赖编译器扩展。它在GCC中的工作速度同样快。它在大多数优化编译器中的工作速度也一样快(我知道英特尔编译器会这样做;不确定微软的东西)。它会在任何编译器上运行,虽然速度较慢。