Dans ce fil, nous examinons des exemples de bonnes utilisations de goto
en C ou C++. C'est inspiré par une réponse que les gens ont votée parce qu'ils pensaient que je plaisantais.
Résumé (l'étiquette d'origine a été modifiée pour rendre l'intention encore plus claire):
infinite_loop:
// code goes here
goto infinite_loop;
Pourquoi c'est mieux que les alternatives:
goto
est la construction de langage qui crée une branche inconditionnelle Les alternatives Dépendent de l’utilisation de structures Supportant des branches conditionnelles, Avec une condition toujours vraie Dégénérée.break
s (Bien qu'il soit encore possible pour un pirate Sans principe de simuler continue
avec un goto
précoce).Règles:
Voyons si nous pouvons en parler comme des grands.
Modifier
Cette question semble terminée maintenant. Cela a généré des réponses de grande qualité. Merci à tout le monde, en particulier ceux qui ont pris mon exemple de petite boucle au sérieux. La plupart des sceptiques étaient concernés par le manque de portée du bloc. Comme @quinmars l'a souligné dans un commentaire, vous pouvez toujours mettre des accolades autour du corps de la boucle Je remarque en passant que for(;;)
et while(true)
ne vous donnent pas les accolades .__ gratuitement, que ce soit (et les omettre peut causer des bugs frustrants). Quoi qu'il en soit, je ne perdrai plus votre énergie cérébrale pour cette bagatelle - je peux vivre avec les noms inoffensifs et idiomatiques for(;;)
et while(true)
(aussi bien si je veux garder mon travail).
En considérant les autres réponses, je vois que beaucoup de gens voient goto
comme quelque chose que vous devez toujours __ réécrire d'une autre manière. Bien sûr, vous pouvez éviter une goto
en introduisant une boucle, un indicateur supplémentaire, une pile de if
s imbriqués ou autre, mais pourquoi ne pas déterminer si goto
est peut-être le meilleur outil pour le travail? Autrement dit, quelle laideur les gens sont-ils prêts à supporter pour éviter d’utiliser une fonctionnalité de langage intégrée aux fins pour lesquelles elle est destinée? À mon avis, même ajouter un drapeau est un prix trop élevé à payer. J'aime que mes variables représentent les choses dans les domaines de problèmes ou de solutions. 'Seulement pour éviter une goto
' ne la coupe pas.
J'accepterai la première réponse qui donnait le motif C pour passer à un bloc de nettoyage. OMI, cela constitue le cas le plus fort pour une goto
de toutes les réponses postées, certainement. Si vous la mesurez par les contorsions qu'un haineux doit subir pour l'éviter.
Heres un tour que j'ai entendu parler de personnes utilisant. Je ne l'ai jamais vu dans la nature cependant. Et cela ne s'applique qu'au C car C++ a RAII pour le faire de manière plus idiomatique.
void foo()
{
if (!doA())
goto exit;
if (!doB())
goto cleanupA;
if (!doC())
goto cleanupB;
/* everything has succeeded */
return;
cleanupB:
undoB();
cleanupA:
undoA();
exit:
return;
}
Le besoin classique de GOTO en C est le suivant
for ...
for ...
if(breakout_condition)
goto final;
final:
Il n'y a pas de moyen simple de sortir des boucles imbriquées sans un goto.
Voici mon exemple non stupide (tiré de Stevens APITUE) pour les appels système Unix pouvant être interrompus par un signal.
restart:
if (system_call() == -1) {
if (errno == EINTR) goto restart;
// handle real errors
}
L'alternative est une boucle dégénérée. Cette version se lit comme en anglais "Si l'appel système a été interrompu par un signal, redémarrez-le".
Si l'appareil de Duff n'a pas besoin d'un goto, vous ne devriez pas le faire non plus! ;)
void dsend(int count) {
int n;
if (!count) return;
n = (count + 7) / 8;
switch (count % 8) {
case 0: do { puts("case 0");
case 7: puts("case 7");
case 6: puts("case 6");
case 5: puts("case 5");
case 4: puts("case 4");
case 3: puts("case 3");
case 2: puts("case 2");
case 1: puts("case 1");
} while (--n > 0);
}
}
code ci-dessus de Wikipedia entry .
Knuth a écrit un article intitulé "Programmation structurée avec instructions GOTO". de ici . Vous trouverez de nombreux exemples ici.
Très commun.
do_stuff(thingy) {
lock(thingy);
foo;
if (foo failed) {
status = -EFOO;
goto OUT;
}
bar;
if (bar failed) {
status = -EBAR;
goto OUT;
}
do_stuff_to(thingy);
OUT:
unlock(thingy);
return status;
}
Le seul cas où j’ai jamais utilisé goto
concerne les sauts en avant, généralement en blocs, et jamais en blocs. Cela évite les abus de do{}while(0)
et d'autres constructions qui augmentent l'imbrication, tout en maintenant un code lisible et structuré.
Je n'ai rien contre les gotos en général, mais je peux penser à plusieurs raisons pour lesquelles vous ne voudriez pas les utiliser pour une boucle comme celle que vous avez mentionnée:
Un bon endroit pour utiliser un goto est une procédure qui peut abandonner à plusieurs reprises, chacune nécessitant différents niveaux de nettoyage. Les gotophobes peuvent toujours remplacer les gotos par du code structuré et une série de tests, mais je pense que cela est plus simple car cela élimine l'indentation excessive:
if (! openDataFile ()) goto quit; if (! getDataFromFile ()) goto closeFileAndQuit; if (! allocateSomeResources) goto freeResourcesAndQuit; // Faites plus de travail ici .... freeResourcesAndQuit: // ressources gratuites closeFileAndQuit: // ferme le fichier quit: // quitter!
@ fizzer.myopenid.com: votre extrait de code posté équivaut à ce qui suit:
while (system_call() == -1)
{
if (errno != EINTR)
{
// handle real errors
break;
}
}
Je préfère définitivement cette forme.
Bien que je déteste ce modèle au fil du temps, il est intégré à la programmation COM.
#define IfFailGo(x) {hr = (x); if (FAILED(hr)) goto Error}
...
HRESULT SomeMethod(IFoo* pFoo) {
HRESULT hr = S_OK;
IfFailGo( pFoo->PerformAction() );
IfFailGo( pFoo->SomeOtherAction() );
Error:
return hr;
}
Voici un exemple de bon goto:
// No Code
J'ai vu qu'il fallait utiliser correctement, mais les situations sont normalement laides. Ce n'est que lorsque l'utilisation de goto
elle-même est tellement moins pire que l'original @ Johnathon Holland le problème est que votre version est moins claire. les gens semblent avoir peur des variables locales:
void foo()
{
bool doAsuccess = doA();
bool doBsuccess = doAsuccess && doB();
bool doCsuccess = doBsuccess && doC();
if (!doCsuccess)
{
if (doBsuccess)
undoB();
if (doAsuccess)
undoA();
}
}
Et je préfère les boucles comme celle-ci, mais certaines personnes préfèrent while(true)
.
for (;;)
{
//code goes here
}
Mon problème, c’est que vous perdez la portée des blocs; toutes les variables locales déclarées entre les gotos restent en vigueur si la boucle est cassée. (Peut-être que vous supposez que la boucle tourne pour toujours; je ne pense pas que ce soit ce que l'auteur de la question d'origine posait, cependant).
Le problème de la portée est plus un problème avec C++, car certains objets peuvent dépendre de leur appel au moment opportun.
Pour moi, la meilleure raison d'utiliser goto est lors d'un processus d'initialisation en plusieurs étapes, où il est essentiel que toutes les entrées soient supprimées en cas d'échec:
if(!foo_init())
goto bye;
if(!bar_init())
goto foo_bye;
if(!xyzzy_init())
goto bar_bye;
return TRUE;
bar_bye:
bar_terminate();
foo_bye:
foo_terminate();
bye:
return FALSE;
#include <stdio.h>
#include <string.h>
int main()
{
char name[64];
char url[80]; /*The final url name with http://www..com*/
char *pName;
int x;
pName = name;
INPUT:
printf("\nWrite the name of a web page (Without www, http, .com) ");
gets(name);
for(x=0;x<=(strlen(name));x++)
if(*(pName+0) == '\0' || *(pName+x) == ' ')
{
printf("Name blank or with spaces!");
getch();
system("cls");
goto INPUT;
}
strcpy(url,"http://www.");
strcat(url,name);
strcat(url,".com");
printf("%s",url);
return(0);
}
Je n'utilise pas de goto moi-même, mais j'ai travaillé avec une personne qui l'utilisait dans des cas spécifiques. Si je me souviens bien, son raisonnement portait sur les problèmes de performance - il avait également des règles spécifiques pour comment . Toujours dans la même fonction, et l'étiquette a toujours été CI-DESSOUS de la déclaration goto.