Exemples de bons gotos en C ou C ++ [fermé]
Question
Dans ce fil de discussion, nous examinons des exemples de bonnes utilisations de goto
en C ou C ++. Il est inspiré par la réponse qui a suscité l'intérêt des internautes car 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 meilleur que les alternatives:
- C'est spécifique.
goto
est le langage construit qui provoque une branche inconditionnelle. Des alternatives dépend de l'utilisation de structures soutenir les branches conditionnelles, avec un toujours dégénéré vrai condition. - L'étiquette documente l'intention sans commentaires supplémentaires.
- Le lecteur n'a pas à scanner le
code intervenant pour les
sauts
précoces (bien qu'il soit encore possible pour un pirate informatique sans principes pour simulercontinue
avec ungoto
précédent.
Règles:
- Imaginez que les gotophobes ne l'aient pas gagner. Il est entendu que ce qui précède ne peut pas être utilisé dans le code réel parce que cela va à l’encontre du langage établi.
- Supposons que nous ayons tous entendu parler de 'Goto considéré comme nuisible' et sais ce goto peut être utilisé pour écrire code spaghetti.
- Si vous n'êtes pas d'accord avec un exemple, critiquer sur le mérite technique seul ('Parce que les gens n'aiment pas goto 'n’est pas une raison technique).
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 petit exemple de 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 boucle. Je remarque en passant que pour (;;)
et tandis que (true)
ne vous donne pas les accolades
gratuitement (et les omettre peut causer des bugs frustrants). En tout cas, je ne vais pas en perdre plus
votre pouvoir cérébral sur cette bagatelle - Je peux vivre avec le inoffensif et idiomatique pour (;;)
et tant que (vrai)
(tout aussi bien si je veux garder mon emploi).
Compte tenu des autres réponses, je constate que de nombreuses personnes considèrent goto
comme quelque chose que vous avez toujours.
avoir à réécrire d'une autre manière. Bien sûr, vous pouvez éviter un goto
en introduisant une boucle,
un drapeau supplémentaire, une pile de si
imbriqués, ou autre chose, mais pourquoi ne pas considérer 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? Ma prise est que
même ajouter un drapeau est un prix trop élevé à payer. J'aime que mes variables représentent des choses dans
le problème ou les domaines de solution. "Seulement pour éviter un goto
" ne le coupe pas.
Je vais accepter la première réponse qui donne le motif C pour passer à un bloc de nettoyage. OMI, cela plaide le plus fort pour un goto
de toutes les réponses postées, certainement
si vous le mesurez par les contorsions qu'un haineux doit traverser pour l'éviter.
La solution
Voici un truc que j'ai entendu parler de gens qui utilisent. Je ne l'ai jamais vu dans la nature cependant. Et cela ne s'applique qu'au C parce que 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;
}
Autres conseils
Le besoin classique de GOTO en C est le suivant
for ...
for ...
if(breakout_condition)
goto final;
final:
Il n’existe aucun moyen simple de sortir des boucles imbriquées sans passer à l'étape suivante.
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 entrée .
Knuth a écrit un article "Programmation structurée avec instructions GOTO", vous pouvez l’obtenir, par exemple. de ici . Vous y trouverez de nombreux exemples.
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 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:
- Cela ne limite pas la portée et toutes les variables temporaires que vous utilisez à l'intérieur ne seront libérées que plus tard.
- Cela ne limite pas la portée et peut donc conduire à des bugs.
- Cela ne limite pas la portée, vous ne pouvez donc pas réutiliser les mêmes noms de variables ultérieurement dans le code de la même portée.
- Cela ne limite pas la portée, vous avez donc la possibilité de sauter une déclaration de variable.
- Les gens n'y sont pas habitués et cela rendra votre code plus difficile à lire.
- Les boucles imbriquées de ce type peuvent conduire à un code spaghetti, les boucles normales ne mènent pas à un code spaghetti.
Un bon endroit pour utiliser un goto est une procédure qui peut abandonner à plusieurs endroits, chacun nécessitant différents niveaux de nettoyage. Les gotophobes peuvent toujours remplacer les gotos par un 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; // Do more work here.... freeResourcesAndQuit: // free resources closeFileAndQuit: // close file quit: // quit!
@ 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.
Même si j'ai tendance à détester ce modèle au fil du temps, il fait partie de 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
est tellement moins mauvaise 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; toute variable locale déclarée entre les gotos reste 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 vital que toutes les entrées soient supprimées en cas d'échec, a la:
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;
Je n'utilise pas de goto moi-même, mais j'ai déjà 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 performances - il avait également des règles spécifiques pour comment . Toujours dans la même fonction, et l'étiquette a toujours été INFÉRIEURE à l'instruction goto.
#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);
}
@Greg:
Pourquoi ne pas faire votre exemple comme ceci:
void foo()
{
if (doA())
{
if (doB())
{
if (!doC())
{
UndoA();
UndoB();
}
}
else
{
UndoA();
}
}
return;
}