Est-il possible de stocker l’adresse d’une étiquette dans une variable et d’utiliser goto pour y accéder?

Je sais que tout le monde déteste les gotos. Dans mon code, pour des raisons que j’ai sockets en compte et avec lesquelles je suis à l’aise, ils apportent une solution efficace en tous cas).

Jusqu’à présent, ils ont été fantastiques, mais je veux étendre la fonctionnalité de telle manière que je dois essentiellement pouvoir stocker des pointeurs sur les étiquettes, puis les consulter plus tard.

Si ce code fonctionnait, il représenterait le type de fonctionnalité dont j’ai besoin. Mais ça ne marche pas, et 30 minutes de googling n’ont rien révélé. Quelqu’un a-t-il une idée?

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

Les normes C et C ++ ne prennent pas en charge cette fonctionnalité. Toutefois, la collection de compilateurs GNU (GCC) inclut une extension non standard pour cela, comme décrit dans cet article . Essentiellement, ils ont ajouté un opérateur spécial “&&” qui rapporte l’adresse de l’étiquette comme type “void *”. Voir l’article pour plus de détails.

PS En d’autres termes, utilisez simplement “&&” au lieu de “&” dans votre exemple, et cela fonctionnera sur GCC.
PPS Je sais que tu ne veux pas que je le dise, mais je vais le dire quand même, … NE FAITES PAS CELA !!!

Vous pouvez faire quelque chose de similaire avec 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; } 

Selon la norme C99, § 6.8.6, la syntaxe pour un goto est la suivante:

     goto identifiant ;

Donc, même si vous pouviez prendre l’adresse d’une étiquette, vous ne pourriez pas l’utiliser avec goto.

Vous pouvez combiner un goto avec un switch , qui est comme un goto calculé, pour un effet similaire:

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

Si vous l’utilisez pour autre chose qu’un concours de C obscurci, je vais vous traquer et vous blesser.

L’instruction switch ... case est essentiellement un goto calculé . Un bon exemple de la façon dont cela fonctionne est le bidouillage bizarre connu sous le nom de 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); } } 

Vous ne pouvez pas faire un goto depuis un emplacement arbitraire en utilisant cette technique, mais vous pouvez envelopper l’intégralité de votre fonction dans une instruction switch basée sur une variable, puis définir cette variable en indiquant où vous voulez aller et goto cette instruction.

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

Bien sûr, si vous faites beaucoup de choses, vous voudrez écrire des macros pour les envelopper.

Cette technique, ainsi que certaines macros pratiques, peuvent même être utilisées pour implémenter des coroutines dans C.

Dans la très très ancienne version du langage C (pensez au temps où les dinosaures parcouraient la Terre), connue sous le nom de “Manuel de référence C” (qui fait référence à un document écrit par Dennis Ritchie), les étiquettes avaient officiellement le type “array of int” (étrange mais vrai), ce qui signifie que vous pouvez déclarer une variable int *

 int *target; 

et atsortingbuer l’adresse de l’étiquette à cette variable

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

Plus tard, vous pouvez utiliser cette variable comme opérande de l’ goto

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

Cependant, dans ANSI C, cette fonctionnalité a été supprimée. Dans le standard moderne C, vous ne pouvez pas prendre l’adresse d’une étiquette et vous ne pouvez pas faire de “paramétrage”. Ce comportement est supposé être simulé avec des instructions de switch , des pointeurs vers des fonctions et d’autres méthodes, etc. En fait, même le “Manuel de référence C” lui-même dit que “Les variables d’étiquette sont une mauvaise idée en général; “(voir ” 14.4 Étiquettes ” ).

Je connais le sentiment alors tout le monde dit que cela ne devrait pas être fait; ça doit être fait. Voici comment procéder:

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

L’opérateur de déréférencement d’étiquettes && ne fonctionnera qu’avec gcc. Et évidemment, la macro d’assemblage jumpto doit être implémentée spécifiquement pour chaque processeur (celui-ci fonctionne avec 32 et 64 bits x86). Gardez également à l’esprit qu’il n’y a aucune garantie que l’état de la stack soit le même à deux points différents dans la même fonction. Et au moins avec une optimisation activée, il est possible que le compilateur suppose que certains registres contiennent une valeur au point après l’étiquette. Ce genre de choses peut facilement être gâché, alors que le compilateur ne s’attend pas à ce qu’il le fasse. Veillez à vérifier la lecture du code compilé.

Je noterai que le fonctionnellement décrit ici (y compris && dans gcc) est IDEAL pour implémenter un interpréteur de langage Forth dans C. Cela élimine tous les arguments «ne fais pas ça» – l’adéquation entre cette fonctionnalité et le chemin L’interprète interne de Forth fonctionne trop bien pour être ignoré.

La seule chose officiellement supscope que vous pouvez faire avec une étiquette en C est de l’obtenir. Comme vous l’avez remarqué, vous ne pouvez pas en prendre l’adresse ou la stocker dans une variable ou autre chose. Donc, au lieu de dire “ne fais pas ça”, je vais dire “tu ne peux pas faire ça”.

On dirait que vous devrez trouver une solution différente. Peut-être le langage d’assemblage, si cela est essentiel pour la performance?

Utilisez des pointeurs de fonctions et une boucle while. Ne faites pas un morceau de code que quelqu’un d’autre devra regretter d’avoir réparé pour vous.

Je suppose que vous essayez de changer l’adresse de l’étiquette d’une manière ou d’une autre. Les pointeurs de fonction fonctionneront.

Lisez ceci: setjmp.h – Wikipedia Comme précédemment dit, il est possible avec setjmp / longjmp avec lequel vous pouvez stocker un jumppoint dans une variable et revenir plus tard.

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

Selon ce sujet , les points d’étiquette ne sont pas un standard, donc, qu’ils fonctionnent ou non, ils dépendent du compilateur que vous utilisez.

Vous pouvez atsortingbuer un libellé à la variable en utilisant &&. Voici votre code modifié.


 int main (void) { int i=1; void* the_label_pointer = &&the_label; the_label: if( i-- ) goto *the_label_pointer; return 0; } 

Vous pouvez faire quelque chose comme le tableau de bord de Fortran avec des pointeurs vers des fonctions.

// variables globales ici

void c1 () {// morceau de code

}

void c2 () {// morceau de code

}

void c3 () {// morceau de code

}

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

// puis
int x = 0;

aller à [x ++] ();

aller à [x ++] ();

aller à [x ++] ();