views:

615

answers:

10

I know everyone hates gotos. In my code, for reasons I have considered and am comfortable with, they provide an effective solution (ie I'm not looking for "don't do that" as an answer, I understand your reservations, and understand why I am using them anyway).

So far they have been fantastic, but I want to expand the functionality in such a way that requires me to essentially be able to store pointers to the labels, then go to them later.

If this code worked, it would represent the type of functionality that I need. But it doesn't work, and 30 min of googling hasn't revealed anything. Does anyone have any ideas?

int main (void)
{
  int i=1;
  void* the_label_pointer;

  the_label:

  the_label_pointer = &the_label;

  if( i-- )
    goto *the_label_pointer;

  return 0;
}
+3  A: 

The only officially supported thing that you can do with a label in C is goto it. As you've noticed, you can't take the address of it or store it in a variable or anything else. So instead of saying "don't do that", I'm going to say "you can't do that".

Looks like you will have to find a different solution. Perhaps assembly language, if this is performance-critical?

Greg Hewgill
+1 for just doing it in assembly, that's how I solved a similar issue previously.
mrduclaw
+11  A: 

You can do something similar with setjmp/longjmp.

int main (void)
{
    jmp_buf buf;
    int i=1;

    // this acts sort of like a dynamic label
    setjmp(buf);

    if( i-- )
        // and this effectively does a goto to the dynamic label
        longjmp(buf, 1);

    return 0;
}
R Samuel Klatchko
Just a caution that setjmp/longjmp can be slow, since they save and restore much more than just the program counter.
RickNZ
+12  A: 

The C and C++ standards do not support this feature. However, the GNU Compiler Collection (GCC) includes a non-standard extension for doing this as described in this article. Essentially, they have added a special operator "&&" that reports the address of the label as type "void*". See the article for details.

P.S. In other words, just use "&&" instead of "&" in your example, and it will work on GCC.
P.P.S. I know you don't want me to say it, but I'll say it anyway,... DON'T DO THAT!!!

Michael Aaron Safyan
+1 for the PPS!
Jonathan Leffler
+1 for the PPS too! I'll add my own: DON'T DO THAT!
Seth Illgard
A: 

According to this thread, label points are not a standard, so whether they work or not would depend on the compiler you're using.

Kaleb Brasee
+1  A: 

Read this: setjmp.h - Wikipedia As previously said it is possible with setjmp/longjmp with which you can store a jumppoint in a variable and jump back later.

+2  A: 

The switch ... case statement is essentially a computed goto. A good example of how it works is the bizarre hack known as Duff's Device:

send(to, from, count)
register short *to, *from;
register count;
{
    register n=(count+7)/8;
    switch(count%8){
    case 0: do{ *to = *from++;
    case 7:  *to = *from++;
    case 6:  *to = *from++;
    case 5:  *to = *from++;
    case 4:  *to = *from++;
    case 3:  *to = *from++;
    case 2:  *to = *from++;
    case 1:  *to = *from++;
     }while(--n>0);
    }
}

You can't do a goto from an arbitrary location using this technique, but you can wrap your entire function in a switch statement based on a variable, then set that variable indicating where you want to go, and goto that switch statement.

int main () {
  int label = 0;
  dispatch: switch (label) {
  case 0:
    label = some_computation();
    goto dispatch;
  case 1:
    label = another_computation();
    goto dispatch;
  case 2:
    return 0;
  }
}

Of course, if you do this a lot, you'd want to write some macros to wrap it.

This technique, along with some convenience macros, can even be used to implement coroutines in C.

Brian Campbell
A: 

Use function pointers and a while loop. Don't make a piece of code someone else will have to regret fixing for you.

I presume you're trying to change the address of the label somehow externally. Function pointers will work.

Nathan
+3  A: 

According to the C99 standard, § 6.8.6, the syntax for a goto is:

    goto identifier ;

So, even if you could take the address of a label, you couldn't use it with goto.

You could combine a goto with a switch, which is like a computed goto, for a similar effect:

int foo() {
    static int i=0;
    return i++;
}

int main(void) {
    enum {
        skip=-1,
        run,
        jump,
        scamper
    } label = skip; 

#define STATE(lbl) case lbl: puts(#lbl); break
    computeGoto:
    switch (label) {
    case skip: break;
     STATE(run);
     STATE(jump);
     STATE(scamper);
    default:
        printf("Unknown state: %d\n", label);
        exit(0);
    }
#undef STATE
    label = foo();
    goto computeGoto;
}

If you use this for anything other than an obfuscated C contest, I will hunt you down and hurt you.

outis
What is the difference between puts(#lbl) and puts(lbl)?
Ahmed Said
The `#` is the preprocessor stringizing operator (http://en.wikipedia.org/wiki/C_preprocessor#Quoting_macro_arguments). It converts identifiers into strings. `puts(lbl)` won't compile because `lbl` isn't a `char *`.
outis
Rather, it will compile with warnings and crash if you run it.
outis
+1 for evil thinking and use of macros above and beyond the call of duty.
EvilTeach
+3  A: 

In the very very very old version of C language (think of the time dinosaurs roamed the Earth), known as "Reference Manual" version of C (which refers to a document written by Dennis Ritchie), labels formally had type "array of int", meaning that you could declare an int * variable

int *target;

and assign the address of label to that variable

target = label; /* where `label` is some label */

Later you could use that variable as the operand of goto statement

goto target; /* jumps to label `label` */

However, in ANSI C this feature was thrown out. In the standard modern C you cannot take address of a label and you cannot do "parametrized" goto. This behavior is supposed to be simulated with switch statements, pointers-to-functions and other methods etc. Actually, even the "Reference Manual" itself said that "Label variables are a bad idea in general; the switch statement makes them almost always unnecessary.".

AndreyT
+1  A: 

Yes, using a label as a value is very easy in GNU C, and can be used to implement a jump table with no assembly. This is in fact the preferred idiom for writing interpreter loops in GCC.

http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Labels-as-Values.html#Labels-as-Values

void *ptr;
ptr = &&foo;
goto *ptr;
...
foo:

You can also use setjmp and longjmp to do this, but do not! setjmp and longjmp are much more inefficient, and are meant for non-local gotos. The whole reason you want computed gotos is for efficiency...

Response to criticisms of this approach:

Some people complain that this is not ANSI C. This is true... but no one in the real world actually sticks to ANSI C, which does not specify many essential things like threads or networking. In almost all large C programs certain bits must be sectioned off and reimplemented for different targets.

Certainly, you should pick and choose carefully where to use non-standard constructs though!

Others think you should never use goto at all... This is just plain wrong. There is nothing wrong whatsoever with using goto in C. For instance, it is very common to use to jump to a cleanup block before returning from a function. Almost all large C code bases have this idiom...

To quote Linus Torvalds:

I think goto's are fine, and they are often more readable than large amounts of indentation.

http://kerneltrap.org/node/553/2131

C is not like other programming languages, and intuition from them will lead you astray.

catphive