webentwicklung-frage-antwort-db.com.de

Ist es möglich, die Adresse eines Etiketts in einer Variablen zu speichern und mit goto dorthin zu springen?

Ich weiß, dass jeder gotos hasst. In meinem Code stellen sie aus Gründen, die ich erwogen habe und mit denen ich vertraut bin, eine effektive Lösung dar (dh ich suche nicht nach "Mach das nicht" als Antwort, ich verstehe Ihre Vorbehalte und verstehe, warum ich sie verwende sowieso).

Bisher waren sie fantastisch, aber ich möchte die Funktionalität so erweitern, dass ich im Wesentlichen Zeiger auf die Etiketten speichern und später darauf zugreifen kann.

Wenn dieser Code funktioniert, würde er die Art von Funktionalität darstellen, die ich benötige. Aber es funktioniert nicht und 30 Minuten googeln haben nichts ergeben. Hat jemand irgendwelche Ideen?

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;
}
48
Joshua Cheek

Die C- und C++ - Standards unterstützen diese Funktion nicht. Die GNU Compiler Collection (GCC) enthält jedoch eine nicht standardmäßige Erweiterung, wie in diesem Artikel beschrieben. Im Wesentlichen haben sie einen speziellen Operator "&&" hinzugefügt, der die Adresse des Labels als Typ "void *" angibt. Einzelheiten finden Sie in dem Artikel.

P.S. Mit anderen Worten, verwenden Sie in Ihrem Beispiel einfach "&&" anstelle von "&", und es funktioniert mit GCC.
P.P.S. Ich weiß, dass du nicht willst, dass ich es sage, aber ich sage es trotzdem ... TUN SIE NICHT !!!

58

Mit setjmp/longjmp können Sie etwas Ähnliches machen.

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

Gemäß dem C99-Standard, § 6.8.6, lautet die Syntax für eine goto:

gehe zukennung;

Selbst wenn Sie die Adresse eines Labels annehmen könnten, können Sie es nicht mit goto verwenden.

Sie könnten eine goto mit einer switch, die einer berechneten goto ähnelt, für einen ähnlichen Effekt kombinieren:

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

Wenn Sie dies für etwas anderes als einen verschleierten C-Wettbewerb verwenden, werde ich Sie verfolgen und Sie verletzen.

12
outis

Die switch ... case-Anweisung ist im Wesentlichen eine berechnete goto . Ein gutes Beispiel dafür ist der bizarre Hack namens 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);
    }
}

Sie können mit dieser Methode keine goto von einem beliebigen Ort aus ausführen. Sie können jedoch Ihre gesamte Funktion in eine switch-Anweisung auf der Grundlage einer Variablen einschließen. Legen Sie dann diese Variable fest und geben Sie an, wohin Sie wollen, und goto diese switch-Anweisung.

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

Wenn Sie dies häufig tun, möchten Sie natürlich einige Makros schreiben, um sie einzuwickeln.

Diese Technik kann zusammen mit einigen Komfortmakros sogar zur Implementierung von Coroutines in C verwendet werden.

9
Brian Campbell

In der sehr sehr alten Version der C-Sprache (denken Sie an die Zeit, in der Dinosaurier die Erde durchstreiften), bekannt als "C Reference Manual" -Version (die sich auf ein Dokument von Dennis Ritchie) bezieht, hatten Labels formal den Typ Array of int "(merkwürdig, aber wahr), was bedeutet, dass Sie eine int *-Variable deklarieren können

int *target;

und weisen Sie der Variablen die Adresse des Labels zu

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

Später könnten Sie diese Variable als Operand der goto-Anweisung verwenden

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

In ANSI C wurde diese Funktion jedoch verworfen. Im modernen Standard C können Sie keine Adresse eines Labels annehmen und "parametrisierte" goto nicht. Dieses Verhalten soll mit switch-Anweisungen, Zeigern auf Funktionen und anderen Methoden simuliert werden. Tatsächlich sagte selbst "C Reference Manual" selbst: "Label-Variablen sind im Allgemeinen eine schlechte Idee, die switch-Anweisung macht sie fast immer unnötig "(siehe " 14.4 Labels " ).

9
AnT

Ich kenne das Gefühl, dann sagen alle, dass es nicht getan werden sollte. es muss einfach hat getan werden. So geht's:

#define jumpto(a) asm("jmp *%0"::"r"(a):)

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

  the_label:

  the_label_pointer = &&the_label;

  if( i-- )
    jumpto(the_label_pointer);

  return 0;
}

Der Label-Dereferenzierungsoperator && funktioniert nur mit gcc. Und natürlich muss das jumpto-Assembly-Makro speziell für jeden Prozessor implementiert werden (dies funktioniert sowohl mit 32 als auch mit 64-Bit-x86). Denken Sie auch daran, dass es keine Garantie gibt, dass der Status des Stapels an zwei verschiedenen Stellen in derselben Funktion gleich ist. Und zumindest bei aktivierter Optimierung ist es möglich, dass der Compiler einige Register an der Stelle hinter dem Label als Wert enthält. Diese Art von Dingen kann leicht vermasselt werden, wenn der verrückte Scheiß den Compiler nicht erwartet. Stellen Sie sicher, dass Sie den kompilierten Code durchgelesen haben.

7
Fabel

Ich möchte anmerken, dass das hier beschriebene (einschließlich && in gcc) funktionell IDEAL ist, um einen Forth-Interpreter in C zu implementieren. Dadurch werden alle Argumente, die das nicht tun, aus dem Wasser geblasen - die Passung zwischen dieser Funktionalität und dem Weg Die inneren Dolmetscherarbeiten von Forth sind zu gut, um sie zu ignorieren.

5
Kip Ingram

Verwenden Sie Funktionszeiger und eine while-Schleife. Machen Sie keinen Code, den jemand anderes für Sie bereuen muss.

Ich gehe davon aus, dass Sie versuchen, die Adresse des Labels irgendwie nach außen zu ändern. Funktionszeiger funktionieren.

4
user208608

Die einzige offiziell unterstützte Sache, die Sie mit einem Label in C machen können, ist goto it. Wie Sie bemerkt haben, können Sie die Adresse nicht nehmen oder in einer Variablen oder irgendetwas anderem speichern. Anstatt also zu sagen "tue das nicht", sage ich "das kannst du nicht".

Sieht aus, als müssten Sie eine andere Lösung finden. Vielleicht Assembler, wenn dies leistungskritisch ist?

3
Greg Hewgill

Lesen Sie dies: setjmp.h - Wikipedia Wie bereits gesagt, ist es mit setjmp/longjmp möglich, mit dem Sie einen Sprungpunkt in einer Variablen speichern und später zurückspringen können.

1
icefex
#include <stdio.h>

int main(void) {

  void *fns[3] = {&&one, &&two, &&three};   
  char p;

  p = -1;

  goto start; end:   return 0;     
  start:   p++;   
  goto *fns[p];
  one:  printf("hello ");  
  goto start;  
  two:  printf("World. \n");  
  goto start;
  three:  goto end;
}
1
RUE_MOHR

Sie können so etwas wie Fortrans Computer-Goto mit Zeigern auf Funktionen tun.

// globale Variablen hier oben

void c1 () {// ein Stück Code 

}

void c2 () {// ein Stück Code 

void c3 () { // Codeabschnitt 

}

void (* goTo [3]) (void) = {c1, c2, c3};

// dann
int x = 0;

goTo [x ++] ();

goTo [x ++] ();

goTo [x ++] ();

0
QuentinUK

Sie können Variablen mit && eine Bezeichnung zuweisen. Hier ist Ihr modifizierter Code.


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

  the_label:


  if( i-- )
    goto *the_label_pointer;


  return 0;
}
0
Mayank

Gemäß diesem Thread sind Labelpunkte kein Standard, also hängt es von dem verwendeten Compiler ab, ob sie funktionieren oder nicht.

0
Kaleb Brasee