Can't understand how storing addresses in void * and jumping to them using goto works

79 Views Asked by At

I'm reading the GNU C manual and reached this section about the goto statement:

As an extension, GCC allows a goto statement to jump to an address specified by a void* variable. To make this work, you also need to take the address of a label by using the unary operator && (not &). Here is a contrived example:

enum Play { ROCK=0, PAPER=1, SCISSORS=2 };
enum Result { WIN, LOSE, DRAW };

static enum Result turn (void) 
{
  const void * const jumptable[] = {&&rock, &&paper, &&scissors};
  enum Play opp;                /* opponent’s play */
  goto *jumptable[select_option (&opp)];
 rock:
  return opp == ROCK ? DRAW : (opp == PAPER ? LOSE : WIN);
 paper:
  return opp == ROCK ? WIN  : (opp == PAPER ? DRAW : LOSE);
 scissors:
  return opp == ROCK ? LOSE : (opp == PAPER ? WIN  : DRAW);
}

What is the second const in const void * const jumptable[] for? The declaration is basically array of const pointers to const void, right? But I can't understand what that means, sorry if it sounds stupid.

Then, what does &opp mean? What does the code do with its address? What is select_option()?

1

There are 1 best solutions below

0
Lundin On

When const is placed on the right side of a pointer * declaration, it makes the pointer itself read-only. If placed left of the *, the pointed-at data is read-only (which doesn't make sense in case of function pointers since functions cannot be read-only, but maybe it does in case of this non-standard GNU spaghetti feature).

When designing conventional jump tables with function pointers rather than labels, it is convention to make the table itself read-only. For 2 reasons:

  • Changing where to jump dynamically in run-time is likely undesirable. Normally you don't want or need that amount of extreme flexibility. So the jump table should be designed at compile-time and stay that way. Intentional or accidental writes to it should be blocked.
  • In embedded systems, the * const read-only qualifier means that the jump table will get allocated in non-volatile flash rather than RAM. Which means that it becomes allocated in true read-only memory, but also that you save valuable RAM - in the general microcontroller programming scenario, RAM is more valuable than flash.

Then, what does &opp mean?

I can't tell for sure since you didn't include the select_option function, but likely it's a function of this nature:

enum Play select_option (enum Play* p)
{
  *p = some_decision();
  return *p;
}

That is, the function changes the passed variable but also returns it for supposed function call convenience. Instead of declaring it with return type void, in which case we'd have to write

select_option (&opp);
goto *jumptable[opp];

Which admittedly is not a problem, since that code is much more readable and the "most sub expressions on a single line wins a price" form of programming is very bad practice.

In general, it is actually considered questionable API design to have a function return the same value through multiple ways, both as return and as one of the parameters. But that's another story.