How to store goto labels in an array and then jump

2020-02-23 07:09发布

问题:

I want to declare an array of "jumplabels".

Then I want to jump to a "jumplabel" in this array.

But I have not any idea how to do this.

It should look like the following code:

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];
}

Does anyone have a idea how to perform this?

回答1:

It is possible with GCC feature known as "labels as values".

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

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

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

It works only with GCC!



回答2:

goto needs a compile-time label.

From this example it seems that you are implementing some kind of state machine. Most commonly they are implemented as a switch-case construct:

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

  /* ... */
}

If you need it to be more dynamic, use an array of function pointers.



回答3:

There's no direct way to store code addresses to jump to in C. How about using switch.

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

You can find similar code produced by every stack-less parser / state machine generator. Such code is not easy to follow so unless it is generated code or your problem is most easily described by state machine I would recommend not do this.



回答4:

could you use function pointers instead of goto?

That way you can create an array of functions to call and call the appropriate one.



回答5:

In plain standard C, this not possible as far as I know. There is however an extension in the GCC compiler, documented here, that makes this possible.

The extension introduces the new operator &&, to take the address of a label, which can then be used with the goto statement.



回答6:

That's what switch statements are for.

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

Note that it's not necessarily translated into a jump table by the compiler.

If you really want to build the jump table yourself, you could use a function pointers array.



回答7:

You might want to look at setjmp/longjmp.



回答8:

You can't do it with a goto - the labels have to be identifiers, not variables or constants. I can't see why you would not want to use a switch here - it will likely be just as efficient, if that is what is concerning you.



回答9:

For a simple answer, instead of forcing compilers to do real stupid stuff, learn good programming practices.



回答10:

Tokenizer? This looks like what gperf was made for. No really, take a look at it.



回答11:

Optimizing compilers (including GCC) will compile a switch statement into a jump table (making a switch statement exactly as fast as the thing you're trying to construct) IF the following conditions are met:

Your switch cases (state numbers) start at zero.

Your switch cases are strictly increasing.

You don't skip any integers in your switch cases.

There are enough cases that a jump table is actually faster (a couple dozen compare-and-gotos in the checking-each-case method of dealing with switch statements is actually faster than a jump table.)

This has the advantage of allowing you to write your code in standard C instead of relying on a compiler extension. It will work just as fast in GCC. It will also work just as fast in most optimizing compilers (I know the Intel compiler does it; not sure about Microsoft stuff). And it will work, although slower, on any compiler.