GOTO toujours considéré comme nuisible? [fermé]

282

Tout le monde est au courant des lettres de Dijkstra à l'éditeur: allez à la déclaration considérée comme nuisible (également ici la transcription .html et ici .pdf) et il y a eu un formidable effort depuis ce temps pour éviter la déclaration goto chaque fois que possible. Bien qu'il soit possible d'utiliser goto pour produire du code tentaculaire incontrôlable, il reste néanmoins dans les langages de programmation modernes . Même la structure de contrôle de continuation avancée de Scheme peut être décrite comme un goto sophistiqué.

Quelles circonstances justifient l'utilisation de goto? Quand est-il préférable d'éviter?

Comme question de suivi: C fournit une paire de fonctions, setjmp et longjmp, qui offrent la possibilité de passer non seulement dans le cadre de pile actuel mais dans n'importe quel cadre appelant. Devraient-ils être considérés aussi dangereux que goto? Plus dangereux?


Dijkstra lui-même regrettait ce titre, dont il n'était pas responsable. À la fin de l' EWD1308 (également ici .pdf), il a écrit:

Enfin une petite histoire pour le disque. En 1968, les communications de l'ACM ont publié un de mes textes sous le titre " La déclaration goto considérée comme nuisible ", qui, dans les années suivantes, serait le plus souvent référencée, malheureusement, souvent, par des auteurs qui n'en avaient pas vu plus que son titre, qui est devenu une pierre angulaire de ma renommée en devenant un modèle: nous verrions toutes sortes d'articles sous le titre "X considéré comme dangereux" pour presque tous les X, y compris un intitulé "Dijkstra considéré comme dangereux". Mais que s'était-il passé? J'avais soumis un document sous le titre " Un cas contre la déclaration goto", qui, afin d'accélérer sa publication, était devenu une" lettre au rédacteur en chef "et, ce faisant, il lui avait donné un nouveau titre de sa propre invention! Le rédacteur en chef était Niklaus Wirth.

Un article classique bien pensé sur ce sujet, à rapprocher de celui de Dijkstra, est la programmation structurée avec go to Statements , de Donald E. Knuth. La lecture permet à la fois de rétablir le contexte et une compréhension non dogmatique du sujet. Dans cet article, l'opinion de Dijkstra sur ce cas est rapportée et est encore plus forte:

Donald E. Knuth: Je crois qu'en présentant un tel point de vue, je ne suis en fait pas en désaccord profond avec les idées de Dijkstra, car il a récemment écrit ce qui suit: "Veuillez ne pas tomber dans le piège de croire que je suis terriblement dogmatique à propos de [la aller à la déclaration]. J'ai le sentiment inconfortable que d'autres en font une religion, comme si les problèmes conceptuels de la programmation pouvaient être résolus par une seule astuce, par une simple forme de discipline de codage! "

MaD70
la source
1
Le goto de C # n'est pas le même que le goto dont Dijkstra parlait, précisément pour les raisons dont Dijkstra a parlé. Ce goto est à la fois nocif comme il l'était à l'époque, mais aussi beaucoup moins nécessaire, car les langages modernes offrent des structures de contrôle alternatives. C # goto est extrêmement contraint.
jalf
25
Les gotos sont bons quand ils ajoutent de la clarté. Si vous avez une longue boucle imbriquée, il peut être préférable de s'en sortir que de définir des variables "break" et de les interrompre jusqu'à ce que vous en sortiez.
simendsjo
28
Si vous avez une boucle imbriquée sur 4 profondeurs (pas que ce soit une bonne chose), la rupture de tout nécessite la définition de valeurs temporaires. Un goto ici est beaucoup plus clair pour moi, et l'IDE devrait facilement montrer où est le goto. Cela dit, l'utilisation de goto devrait être clairsemée, et à mon avis, ne passer que pour sauter le code
simendsjo
7
Je vous suggère d'aller lire les neuf mille et un fils étiquetés goto.
Matti Virkkunen
5
Il y a une chose clairement pire que d'utiliser goto: pirater ensemble des outils de programmation structurés pour implémenter a goto.

Réponses:

184

Les déclarations suivantes sont des généralisations; bien qu'il soit toujours possible de plaider l'exception, elle ne vaut généralement pas (selon mon expérience et mon humble avis) les risques.

  1. L'utilisation sans contrainte des adresses mémoire (GOTO ou pointeurs bruts) offre trop de possibilités de faire des erreurs facilement évitables.
  2. Plus il y a de façons d'arriver à un "emplacement" particulier dans le code, moins on peut être sûr de l'état du système à ce stade. (Voir ci-dessous.)
  3. La programmation structurée à mon humble avis consiste moins à «éviter les GOTO» qu'à faire en sorte que la structure du code corresponde à la structure des données. Par exemple, une structure de données répétitive (par exemple un tableau, un fichier séquentiel, etc.) est naturellement traitée par une unité de code répétée. Avoir des structures intégrées (par exemple while, for, until, for-each, etc.) permet au programmeur d'éviter l'ennui de répéter les mêmes modèles de code clichés.
  4. Même si GOTO est un détail d'implémentation de bas niveau (pas toujours le cas!), C'est en dessous du niveau auquel le programmeur devrait penser. Combien de programmeurs équilibrent leurs chéquiers personnels en binaire brut? Combien de programmeurs s'inquiètent du secteur du disque contenant un enregistrement particulier, au lieu de simplement fournir une clé à un moteur de base de données (et de combien de façons les choses pourraient-elles mal tourner si nous écrivions vraiment tous nos programmes en termes de secteurs de disque physique)?

Notes de bas de page ci-dessus:

Concernant le point 2, considérons le code suivant:

a = b + 1
/* do something with a */

Au point "faire quelque chose" dans le code, nous pouvons affirmer avec une confiance élevée qui aest supérieure à b. (Oui, j'ignore la possibilité d'un débordement d'entier non pris au piège. Ne nous enlisons pas un exemple simple.)

D'un autre côté, si le code avait lu de cette façon:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

La multiplicité des façons d'arriver au label 10 signifie que nous devons travailler beaucoup plus dur pour avoir confiance dans les relations entre aet bà ce moment-là. (En fait, dans le cas général, c'est indécidable!)

En ce qui concerne le point 4, toute la notion "d'aller quelque part" dans le code n'est qu'une métaphore. Rien ne va vraiment "nulle part" à l'intérieur du CPU, sauf les électrons et les photons (pour la chaleur perdue). Parfois, nous abandonnons une métaphore pour une autre, plus utile, une. Je me souviens avoir rencontré (il y a quelques décennies!) Une langue où

if (some condition) {
  action-1
} else {
  action-2
}

a été implémenté sur une machine virtuelle en compilant action-1 et action-2 en tant que routines sans paramètres hors ligne, puis en utilisant un seul opcode VM à deux arguments qui a utilisé la valeur booléenne de la condition pour invoquer l'une ou l'autre. Le concept était simplement de «choisir quoi invoquer maintenant» plutôt que «aller ici ou y aller». Encore une fois, juste un changement de métaphore.

joel.neely
la source
2
Un bon point. Dans les langages de niveau supérieur, goto ne veut rien dire (pensez à sauter entre les méthodes en Java). Une fonction Haskell peut consister en une seule expression; essayez de sauter de là avec un goto!
Escargot mécanique
2
Postscript fonctionne comme votre exemple de point 4.
luser droog
Smalltalk fonctionne de manière similaire à l'exemple du point 4, si par "de manière similaire" vous voulez dire "rien de tel que les langages procéduraux". : P Il n'y a pas de contrôle de flux dans la langue; toutes les décisions sont gérées par polymorphisme ( trueet falsesont de types différents), et chaque branche d'un if / else est fondamentalement un lambda.
cHao
Ce sont des points valables, mais en fin de compte, ils réitèrent simplement à quel point le goto peut être «mal utilisé». Break, continue, exit, return, gosub, settimeout, global, include, etc. sont toutes des techniques modernes qui nécessitent de suivre mentalement le flux des choses et peuvent toutes être utilisées à mauvais escient pour créer du code spaghetti et sauter pour créer une incertitude des états variables. Pour être juste, même si je n'ai jamais vu une mauvaise utilisation de goto de première main, je ne l'ai également vu qu'une ou deux fois. Cela témoigne de l'affirmation selon laquelle il y a toujours de meilleures choses à utiliser.
Beejor
gotodans un langage de programmation moderne (Go) stackoverflow.com/a/11065563/3309046 .
nishanths
245

Bande dessinée GOTO de XKCD

Un de mes collègues a dit que la seule raison d'utiliser un GOTO était si vous vous programmiez si loin dans un coin que c'est la seule issue. En d'autres termes, une conception appropriée à l'avance et vous n'aurez pas besoin d'utiliser un GOTO plus tard.

Je pensais que cette bande dessinée illustre magnifiquement "Je pourrais restructurer le flux du programme, ou utiliser un petit" GOTO "à la place." Un GOTO est une voie de sortie faible lorsque votre conception est faible. Les vélociraptors s'attaquent aux faibles .

Jim McKeeth
la source
41
GOTO peut faire «sauter» d'un endroit arbitraire à un autre endroit arbitraire. Velociraptor a sauté d'ici de nulle part!
rpattabi
29
Je ne trouve pas du tout la blague drôle parce que tout le monde sait que vous devez également créer un lien avant de pouvoir exécuter.
Kinjal Dixit
31
Votre collègue a tort et n'a évidemment pas lu le document de Knuth.
Jim Balter
27
Je n'ai vu aucune fin de code obscurci et autrement déformé au fil des ans mis en place juste pour éviter un goto. @jimmcKeeth, le dessin animé xkcd ci-dessus n'établit pas que goto est faible. Il se moque de l'hystérie autour de son utilisation.
4
Vous vous rendez compte que le code source de la bibliothèque Delphi contient des instructions goto. Et ce sont des utilisations appropriées de goto.
David Heffernan
131

Parfois, il est valide d'utiliser GOTO comme alternative à la gestion des exceptions dans une seule fonction:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

Le code COM semble tomber dans ce modèle assez souvent.

Rob Walker
la source
29
Je suis d'accord, il y a des cas d'utilisation légitimes où, goto peut simplifier le code et le rendre plus lisible / maintenable, mais il semble y avoir une sorte de goto-phobie flottant ...
Pop Catalin
48
@Bob: Il est difficile de déplacer le code err_cleanup dans un sous-programme s'il nettoie des variables locales.
Niki
4
En fait, je l'ai utilisé dans COM / VB6 juste parce que je n'avais pas d' alternative, pas parce que c'était une alternative. Comme je suis heureux de nos jours avec try / catch / enfin.
Rui Craveiro
10
@ user4891 La manière idiomatique C ++ n'est pas d'essayer {} catch () {cleanup; }, mais plutôt RAII, où les ressources qui doivent être nettoyées le sont dans les destructeurs. Chaque constructeur / destructeur gère exactement une ressource.
David Stone
5
Il y a deux façons d'écrire ceci en C sans goto; et les deux sont beaucoup plus courts. Soit: if (f ()) if (g ()) if (h ()) return success; nettoyer(); échec de retour; ou: si (f () && g () && h ()) renvoient le succès; nettoyer(); échec de retour;
LHP
124

Je ne me souviens d'avoir utilisé un goto qu'une seule fois. J'avais une série de cinq boucles comptées imbriquées et je devais pouvoir sortir de la structure entière de l'intérieur tôt en fonction de certaines conditions:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

J'aurais pu facilement déclarer une variable de coupure booléenne et l'utiliser dans le cadre du conditionnel pour chaque boucle, mais dans ce cas, j'ai décidé qu'un GOTO était tout aussi pratique et tout aussi lisible.

Aucun vélociraptor ne m'a attaqué.

shsteimer
la source
83
"Refactorisez-le dans une fonction et remplacez goto par return :)", et la différence est? quelle est vraiment la différence? n'est-ce pas aussi un retour? Les retours freinent également le flux structuré de goto, et dans ce cas, ils le font de la même manière (même si goto peut être utilisé pour des choses plus méchantes)
Pop Catalin
38
L'imbrication de nombreuses boucles est généralement une odeur de code qui lui est propre. À moins que vous ne fassiez, comme la multiplication de tableaux à 5 dimensions, il est difficile d'imaginer une situation où certaines des boucles internes ne pourraient pas être utilement extraites dans des fonctions plus petites. Comme toutes les règles de base, il y a une poignée d'exceptions, je suppose.
Doug McClean
5
Le remplacer par un retour ne fonctionne que si vous utilisez une langue qui prend en charge les retours.
Loren Pechtel
31
@leppie: La génération qui s'est rebellée contre gotonous et nous a donné une programmation structurée a également rejeté les premiers retours, pour la même raison. Cela dépend de la lisibilité du code, de la clarté avec laquelle il exprime l'intention du programmeur. Créer une fonction dans un autre but que d'éviter d'utiliser un mot-clé décrié entraîne une mauvaise cohésion: le remède est pire que la maladie.
Mud
21
@ButtleButkus: Franchement, c'est tout aussi mauvais, sinon pire. Au moins avec a goto, on peut spécifier explicitement la cible. Avec break 5;, (1) je dois compter les fermetures de boucle pour trouver la destination; et (2) si la structure de la boucle change un jour, il peut très bien nécessiter de changer ce nombre afin de maintenir la destination correcte. Si je veux éviter goto, le gain devrait être de ne pas avoir à suivre manuellement des trucs comme ça.
cHao
93

Nous avons déjà eu cette discussion et je maintiens mon propos .

De plus, je en ai marre des gens qui décrivent des structures linguistiques de niveau supérieur comme « gotodéguisé » , parce qu'ils ont manifestement pas obtenu le point du tout . Par exemple:

Même la structure de contrôle de continuation avancée de Scheme peut être décrite comme un goto sophistiqué.

C'est un non-sens complet. Chaque structure de contrôle peut être implémentée en termes de gotomais cette observation est tout à fait triviale et inutile. goton'est pas considéré comme nuisible en raison de ses effets positifs mais en raison de ses conséquences négatives et celles-ci ont été éliminées par une programmation structurée.

De même, dire «GOTO est un outil, et comme tous les outils, il peut être utilisé et abusé» est complètement faux. Aucun travailleur de la construction moderne n'utiliserait un rocher et ne dirait qu'il est «un outil». Les roches ont été remplacées par des marteaux. gotoa été remplacé par des structures de contrôle. Si le travailleur de la construction était coincé dans la nature sans marteau, il utiliserait bien sûr un rocher à la place. Si un programmeur doit utiliser un langage de programmation inférieur qui n'a pas la fonctionnalité X, eh bien, bien sûr, il se peut qu'il doive l'utiliser à la gotoplace. Mais si elle l'utilise ailleurs au lieu de la fonction de langue appropriée, elle n'a clairement pas bien compris la langue et l'utilise à tort. C'est vraiment aussi simple que ça.

Konrad Rudolph
la source
82
Bien sûr, la bonne utilisation d'un rocher n'est pas comme un marteau. L'une de ses utilisations appropriées est une meule ou pour affûter d'autres outils. Même la roche humble, lorsqu'elle est utilisée correctement, est un bon outil. Il vous suffit de trouver la bonne utilisation. Il en va de même pour goto.
Kibbee
10
Alors, quelle est la bonne utilisation de Goto? Pour chaque cas imaginable, il existe un autre outil mieux adapté. Et même votre meule est en fait remplacée par des outils de haute technologie de nos jours, même si elles sont encore faites de roche. Il y a une grande différence entre une matière première et un outil.
Konrad Rudolph
8
@jalf: Aller certainement fait exist en C #. Voir stackoverflow.com/questions/359436/…
Jon Skeet
51
Je suis consterné par le fait que beaucoup de gens approuvent ce post. Votre message ne semblait efficace que parce que vous n'avez jamais pris la peine de remettre en question la logique que vous exécutiez réellement, vous n'avez donc pas remarqué votre erreur. Permettez-moi de paraphraser l'intégralité de votre message: "Il existe un outil supérieur pour un goto dans chaque situation, donc les gotos ne doivent jamais être utilisés." C'est une condition biconditionnelle logique, et en tant que tel, votre message entier pose essentiellement la question "Comment savez-vous qu'il existe un outil supérieur pour un goto dans chaque situation?"
Codage avec style du
10
@Coding: Non, vous avez complètement raté l'essentiel de la publication. C'était une riposte plutôt qu'un argument isolé et complet. J'ai simplement souligné l'erreur de l'argument principal «pour» goto. Vous avez raison dans la mesure où je n'offre aucun argument contre en gotosoi - je n'avais pas l'intention de le faire - il n'y a donc pas de question à poser.
Konrad Rudolph
91

Goto est extrêmement bas sur ma liste de choses à inclure dans un programme juste pour le plaisir. Cela ne veut pas dire que c'est inacceptable.

Goto peut être agréable pour les machines d'état. Une instruction switch dans une boucle est (par ordre d'importance typique): (a) pas réellement représentative du flux de contrôle, (b) moche, (c) potentiellement inefficace selon la langue et le compilateur. Vous finissez donc par écrire une fonction par état et à faire des choses comme "return NEXT_STATE;" qui ressemblent même à goto.

Certes, il est difficile de coder les machines à états de manière à les rendre faciles à comprendre. Cependant, aucune de ces difficultés n'est liée à l'utilisation de goto, et rien de tout cela ne peut être réduit en utilisant des structures de contrôle alternatives. À moins que votre langue n'ait une construction de «machine à états». La mienne non.

Dans les rares occasions où votre algorithme est vraiment plus compréhensible en termes de chemin à travers une séquence de nœuds (états) connectés par un ensemble limité de transitions autorisées (gotos), plutôt que par un flux de contrôle plus spécifique (boucles, conditions, etc.) ), alors cela devrait être explicite dans le code. Et vous devez dessiner un joli diagramme.

setjmp / longjmp peut être utile pour implémenter des exceptions ou des comportements de type exception. Bien qu'elles ne soient pas universellement louées, les exceptions sont généralement considérées comme une structure de contrôle "valide".

setjmp / longjmp sont «plus dangereux» que goto dans le sens où ils sont plus difficiles à utiliser correctement, peu importe de façon compréhensible.

Il n'y a jamais eu et il n'y aura jamais de langage dans lequel il est le moins difficile d'écrire du mauvais code. - Donald Knuth.

Retirer goto de C ne faciliterait pas l'écriture de bon code en C. En fait, il serait préférable de passer à côté du fait que C est censé être capable d'agir comme un langage assembleur glorifié.

Ce sera ensuite "les pointeurs considérés comme nuisibles", puis "le typage du canard considéré comme nuisible". Alors, qui restera pour vous défendre quand ils viendront vous enlever votre construction de programmation dangereuse? Hein?

Steve Jessop
la source
14
, Personellement c'est le commentaire que j'aurais donné le chèque. Une chose que je voudrais signaler aux lecteurs est que le terme ésotérique "machines à états" inclut des choses quotidiennes comme les analyseurs lexicaux. Découvrez la sortie de lex somtime. Plein de gotos.
TED
2
Vous pouvez utiliser une instruction switch à l'intérieur d'une boucle (ou gestionnaire d'événements) pour faire très bien les machines à états. J'ai fait beaucoup de machines à états sans avoir à utiliser de jmp ou de goto.
Scott Whitlock
11
+1 Ces flèches sur les machines d'état correspondent à "goto" plus étroitement qu'à toute autre structure de contrôle. Bien sûr, vous pouvez utiliser un commutateur dans une boucle - tout comme vous pouvez utiliser un tas de gotos au lieu d'un certain temps pour d'autres problèmes, mais c'est généralement une idée; c'est tout l'intérêt de cette discussion.
Edmund
2
Puis-je vous citer sur ce dernier paragraphe?
Chris Lutz
2
Et, ici en 2013, nous avons déjà franchi (et dépassé un peu) la phase des "pointeurs considérés comme nuisibles".
Isiah Meadows
68

Sous Linux: Utilisation de goto In Kernel Code sur Kernel Trap, il y a une discussion avec Linus Torvalds et un "nouveau mec" sur l'utilisation des GOTO dans le code Linux. Il y a de très bons points là-bas et Linus vêtu de cette arrogance habituelle :)

Quelques passages:

Linus: "Non, vous avez été soumis à un lavage de cerveau par des personnes CS qui pensaient que Niklaus Wirth savait réellement de quoi il parlait. Il ne l'a pas fait. Il n'a pas la moindre idée."

-

Linus: "Je pense que les goto sont très bien, et ils sont souvent plus lisibles que de grandes quantités d'indentation."

-

Linus: "Bien sûr, dans des langages stupides comme Pascal, où les étiquettes ne peuvent pas être descriptives, les goto peuvent être mauvais."

Marcio Aguiar
la source
9
C'est un bon point comment? Ils discutent de son utilisation dans une langue qui n'a rien d'autre. Lorsque vous programmez en assembleur, toutes les branches et les sauts sont des goto. Et C est, et était, un "langage d'assemblage portable". De plus, les passages que vous citez dire rien au sujet de la raison pour laquelle il pense goto est bon.
jalf
5
Sensationnel. C'est décevant à lire. On pourrait penser qu'un grand gars du système d'exploitation comme Linus Torvalds serait mieux que de dire cela. Pascal (pascal à l'ancienne, pas la version Object moderne) était ce dans quoi Mac OS Classic était écrit pendant la période 68k, et c'était le système d'exploitation le plus avancé de son temps.
Mason Wheeler
6
@mason Classic Mac OS avait quelques bibliothèques Pascal (finalement - le runtime Pascal a pris trop de mémoire dans les premiers Mac) mais la majorité du code principal a été écrit dans Assembler, en particulier les routines graphiques et d'interface utilisateur.
Jim Dovey
30
Linus ne plaide (explicitement, comme Rik van Riel dans cette discussion) pour goto pour gérer le statut de sortie, et il le fait sur la base de la complexité que les constructions alternatives de C apporteraient si elles étaient utilisées à la place.
Charles Stewart
21
À mon humble avis, Linus a raison sur cette question. Son point est que le code du noyau, écrit en C, qui doit implémenter quelque chose de similaire à la gestion des exceptions, est écrit le plus clairement et simplement en utilisant un goto. L'idiome goto cleanup_and_exitest l'une des rares "bonnes" utilisations de goto à gauche maintenant que nous avonsfor , whileet ifde gérer notre flux de contrôle. Voir aussi: programmers.stackexchange.com/a/154980
steveha
50

En C, gotone fonctionne que dans le cadre de la fonction actuelle, ce qui tend à localiser les éventuels bugs. setjmpet longjmpsont beaucoup plus dangereux, étant non locaux, compliqués et dépendants de la mise en œuvre. En pratique cependant, ils sont trop obscurs et peu courants pour causer de nombreux problèmes.

Je crois que le danger de gotoC est grandement exagéré. N'oubliez pas que les gotoarguments originaux remontaient à l'époque des langages comme BASIC à l'ancienne, où les débutants écrivaient du code spaghetti comme ceci:

3420 IF A > 2 THEN GOTO 1430

Ici, Linus décrit une utilisation appropriée de goto: http://www.kernel.org/doc/Documentation/CodingStyle (chapitre 7).

smh
la source
7
Lorsque BASIC était disponible pour la première fois, il n'y avait pas d'alternative à GOTO nnnn et GOSUB mmmm comme moyen de sauter. Des constructions structurées ont été ajoutées plus tard.
Jonathan Leffler
7
Vous manquez le point ... même là, vous n'avez pas eu à écrire des spaghettis ... vos GOTO pourraient toujours être utilisés de manière disciplinée
JoelFan
Il convient également de noter que le comportement de setjmp/ longjmpn'a été spécifié que lorsqu'ils ont été utilisés comme moyen de sauter à un endroit dans une étendue à partir d'autres endroits dans cette même étendue. Une fois que le contrôle quitte la portée où il setjmpest exécuté, toute tentative d'utilisation longjmpsur la structure créée par setjmpentraînera un comportement indéfini.
supercat
9
Certaines versions de BASIC vous le permettraient GOTO A * 40 + B * 200 + 30. Il n'est pas difficile de voir comment cela était très pratique et très dangereux.
Jon Hanna
1
@Hjulle, il calculerait l'expression et irait ensuite à la ligne de code avec ce numéro (les numéros de ligne explicites étaient une exigence de la plupart des dialectes antérieurs). ZX Spectrum Basic était celui qui accepterait cela
Jon Hanna
48

Aujourd'hui, il est difficile de voir grand-chose à propos de la GOTOdéclaration, car les gens de la "programmation structurée" ont surtout gagné le débat et les langages d'aujourd'hui ont suffisamment de structures de flux de contrôle à éviter GOTO.

Comptez le nombre de gotos dans un programme C moderne. Maintenant , ajoutez le nombre de break, continueetreturn des déclarations. De plus, ajoutez le nombre de fois que vous utilisezif , else,while , switchou case. C'est à peu près combien GOTOvotre programme aurait eu si vous écriviez en FORTRAN ou BASIC en 1968 lorsque Dijkstra a écrit sa lettre.

À l'époque, les langages de programmation manquaient de flux de contrôle. Par exemple, dans le Dartmouth BASIC d'origine:

  • IF les déclarations n'avaient pas ELSE . Si vous en vouliez un, vous deviez écrire:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Même si votre IF relevé n'avait pas besoin d'un ELSE, il était toujours limité à une seule ligne, qui consistait généralement en un GOTO.

  • Il n'y avait pas DO...LOOP déclaration. Pour les nonFOR boucles, vous deviez terminer la boucle par un explicite GOTOou IF...GOTOrevenir au début.

  • Il n'y avait pas SELECT CASE . Vous deviez utiliserON...GOTO .

Donc, vous vous êtes retrouvé avec beaucoup de GOTOs dans votre programme. Et vous ne pouviez pas dépendre de la restriction de GOTOs à un seul sous-programme (carGOSUB...RETURN c'était un concept si faible de sous-programmes), donc cesGOTO s pouvaient aller n'importe où . De toute évidence, cela rendait le flux de contrôle difficile à suivre.

C'est de là que vient l'anti- GOTOmouvement.

dan04
la source
Une autre chose à noter est que la façon préférée d'écrire du code si l'on a du code dans une boucle qui devrait rarement s'exécuter serait 420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]// 3230 goto 430. L'écriture de code de cette façon évite toutes les branches ou sauts pris dans le cas de la ligne principale commune, mais cela rend les choses difficiles à suivre. L'évitement des branches dans le code assembleur peut être pire, si certaines branches sont limitées par exemple à +/- 128 octets et n'ont parfois pas de paires complémentaires (par exemple "cjne" existe mais pas "cje").
supercat
J'ai écrit une fois du code pour le 8x51 qui avait une interruption qui a fonctionné une fois tous les 128 cycles. Chaque cycle supplémentaire passé dans le cas courant de cet ISR réduirait la vitesse d'exécution du code de la ligne principale de plus de 1% (je pense qu'environ 90 cycles sur 128 étaient généralement disponibles pour la ligne principale), et toute instruction de branchement prendrait deux cycles ( dans les cas de ramification et de chute). Le code comportait deux comparaisons - l'une qui rapportait généralement l'égalité; l'autre, non égal. Dans les deux cas, le code des cas rares était à plus de 128 octets. Alors ...
supercat
cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough// `ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: .... Ce n'est pas un très bon modèle de codage, mais lorsque des cycles individuels comptent, on fait de telles choses. Bien sûr, dans les années 1960, ces niveaux d'optimisation étaient plus importants qu'aujourd'hui, d'autant plus que les processeurs modernes nécessitent souvent des optimisations étranges, et que les systèmes de compilation juste à temps peuvent être en mesure d'appliquer du code d'optimisation pour les processeurs qui n'existaient pas lorsque le le code en question a été écrit.
supercat
34

Go To peut fournir une sorte de remplaçant pour la gestion des exceptions "réelles" dans certains cas. Considérer:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

Évidemment, ce code a été simplifié pour prendre moins de place, alors ne vous attardez pas trop sur les détails. Mais considérons une alternative que j'ai vue trop souvent dans le code de production par des codeurs allant à des longueurs absurdes pour éviter d'utiliser goto:

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

Maintenant fonctionnellement, ce code fait exactement la même chose. En fait, le code généré par le compilateur est presque identique. Cependant, dans le zèle du programmeur pour apaiser Nogoto (le dieu redouté de la réprimande académique), ce programmeur a complètement brisé l'idiome sous-jacent que whilereprésente la boucle, et a fait un vrai nombre sur la lisibilité du code. Ce n'est pas mieux.

Donc, la morale de l'histoire est que si vous vous retrouvez à recourir à quelque chose de vraiment stupide pour éviter d'utiliser goto, alors ne le faites pas.

tylerl
la source
1
Bien que j'ai tendance à être d'accord avec ce que vous dites ici, le fait que les breakdéclarations se trouvent à l'intérieur d'une structure de contrôle montre clairement ce qu'elles font. Avec l' gotoexemple, la personne qui lit le code doit parcourir le code pour trouver l'étiquette, qui pourrait en théorie se trouver avant la structure de contrôle. Je n'ai pas assez d'expérience avec le C à l'ancienne pour juger que l'un est définitivement meilleur que l'autre, mais il y a des compromis dans les deux cas.
Vivian River
5
@DanielAllenLangdon: Le fait que les breaks soient à l'intérieur d'une boucle indique clairement qu'ils sortent de la boucle . Ce n'est pas "exactement ce qu'ils font", car en réalité, il n'y a pas du tout de boucle! Rien n'y a jamais une chance de se répéter, mais ce n'est pas clair jusqu'à la fin. Le fait que vous ayez une "boucle" qui ne s'exécute jamais plus d'une fois signifie que les structures de contrôle sont abusées. Avec l' gotoexemple, le programmeur peut dire goto error_handler;. Il est plus explicite et encore moins difficile à suivre. (Ctrl + F, "error_handler:" pour trouver la cible. Essayez de faire cela avec "}".)
cHao
1
J'ai vu une fois un code similaire à votre deuxième exemple dans un système de contrôle de la circulation aérienne - parce que «goto n'est pas dans notre lexique».
QED
30

Donald E. Knuth a répondu à cette question dans le livre "Literate Programming", 1992 CSLI. Dans. 17 il y a un essai " Programmation structurée avec des instructions goto " (PDF). Je pense que l'article pourrait également avoir été publié dans d'autres livres.

L'article décrit la suggestion de Dijkstra et décrit les circonstances dans lesquelles elle est valable. Mais il donne également un certain nombre de contre-exemples (problèmes et algorithmes) qui ne peuvent pas être facilement reproduits en utilisant uniquement des boucles structurées.

L'article contient une description complète du problème, l'historique, des exemples et des contre-exemples.

Bruno Ranschaert
la source
25

Goto a jugé utile.

J'ai commencé la programmation en 1975. Pour les programmeurs de l'ère des années 1970, les mots "goto considérés comme nuisibles" indiquaient plus ou moins que de nouveaux langages de programmation avec des structures de contrôle modernes valaient la peine d'être essayés. Nous avons essayé les nouvelles langues. Nous nous sommes rapidement convertis. Nous ne sommes jamais revenus.

Nous ne sommes jamais revenus, mais si vous êtes plus jeune, vous n'y êtes jamais allé.

Maintenant, une expérience dans les anciens langages de programmation peut ne pas être très utile, sauf comme indicateur de l'âge du programmeur. Néanmoins, les jeunes programmeurs n'ont pas cette formation, ils ne comprennent donc plus le message véhiculé par le slogan "goto considéré comme nuisible" à son public cible au moment de son introduction.

Les slogans que l'on ne comprend pas ne sont pas très éclairants. Il vaut probablement mieux oublier de tels slogans. De tels slogans n'aident pas.

Ce slogan particulier, "Goto considéré comme nuisible", a pris sa propre vie.

Ne peut-on pas abuser de goto? Réponse: bien sûr, mais alors quoi? Pratiquement chaque élément de programmation peut être abusé. Les humblesbool par exemple, sont abusés plus souvent que certains d'entre nous voudraient le croire.

En revanche, je ne me souviens pas avoir rencontré un seul cas réel d'abus de goto depuis 1990.

Le plus gros problème avec goto n'est probablement pas technique mais social. Les programmeurs qui ne savent pas grand-chose semblent parfois penser que déprécier goto les rend intelligents. Vous devrez peut-être satisfaire de tels programmeurs de temps en temps. C'est la vie.

Le pire à propos de goto aujourd'hui, c'est qu'il n'est pas assez utilisé.

thb
la source
24

Attiré par Jay Ballou ajoutant une réponse, je vais ajouter mes 0,02 £. Si Bruno Ranschaert ne l'avait pas déjà fait, j'aurais mentionné l'article de "Programmation structurée avec instructions GOTO" de Knuth.

Une chose que je n'ai pas vue discutée est le type de code qui, bien que pas exactement commun, a été enseigné dans les manuels de Fortran. Des choses comme la gamme étendue d'une boucle DO et de sous-programmes à code ouvert (rappelez-vous, ce serait Fortran II, ou Fortran IV, ou Fortran 66 - pas Fortran 77 ou 90). Il y a au moins une chance que les détails syntaxiques soient inexacts, mais les concepts doivent être suffisamment précis. Les extraits dans chaque cas sont à l'intérieur d'une seule fonction.

Notez que l'excellent mais daté (et épuisé) livre « The Elements of Programming Style, 2nd Edn '' de Kernighan & Plauger comprend des exemples concrets d'abus de GOTO dans la programmation de manuels de son époque (fin des années 70). Le matériel ci-dessous n'est cependant pas de ce livre.

Plage étendue pour une boucle DO

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

L'une des raisons de ces absurdités était la bonne carte perforée à l'ancienne. Vous remarquerez peut-être que les étiquettes (bien désordonnées car c'était un style canonique!) Sont dans la colonne 1 (en fait, elles devaient être dans les colonnes 1-5) et le code est dans les colonnes 7-72 ​​(la colonne 6 était la suite colonne marqueur). Les colonnes 73 à 80 recevraient un numéro de séquence, et il y avait des machines qui triaient les jeux de cartes perforées dans l'ordre des numéros de séquence. Si vous aviez votre programme sur des cartes séquencées et que vous deviez ajouter quelques cartes (lignes) au milieu d'une boucle, vous auriez à tout repiquer après ces lignes supplémentaires. Cependant, si vous avez remplacé une carte par le truc GOTO, vous pourriez éviter de reséquencer toutes les cartes - vous venez de ranger les nouvelles cartes à la fin de la routine avec de nouveaux numéros de séquence. Considérez qu'il s'agit de la première tentative d'informatique verte

Oh, vous pourriez également noter que je triche et ne crie pas - Fortran IV a été écrit en majuscules normalement.

Sous-programme à code ouvert

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

Le GOTO entre les étiquettes 76 et 54 est une version du goto calculé. Si la variable i a la valeur 1, passez à la première étiquette de la liste (123); s'il a la valeur 2, passez à la seconde, et ainsi de suite. Le fragment de 76 au goto calculé est le sous-programme à code ouvert. C'était un morceau de code exécuté un peu comme un sous-programme, mais écrit dans le corps d'une fonction. (Fortran avait également des fonctions de déclaration - qui étaient des fonctions intégrées qui tenaient sur une seule ligne.)

Il y avait des constructions pires que le goto calculé - vous pouviez attribuer des étiquettes aux variables et ensuite utiliser un goto assigné. Googling assigné goto me dit qu'il a été supprimé de Fortran 95. Craquez pour la révolution de la programmation structurée qui pourrait à juste titre avoir commencé en public avec la lettre ou l'article "GOTO Considéré Nocif" de Dijkstra.

Sans une certaine connaissance du genre de choses qui ont été faites à Fortran (et dans d'autres langues, dont la plupart sont à juste titre tombées à l'eau), il est difficile pour nous, nouveaux arrivants, de comprendre l'ampleur du problème auquel Dijkstra était confronté. Heck, je n'ai commencé à programmer que dix ans après la publication de cette lettre (mais j'ai eu le malheur de programmer en Fortran IV pendant un certain temps).

Jonathan Leffler
la source
2
Si vous voulez voir un exemple de code utilisant goto`` à l'état sauvage '', la question Wanted: Working Bose-Hibbard Sort Algorithm montre du code (Algol 60) publié en 1963. La disposition d'origine n'est pas comparable aux normes de codage modernes. Le code clarifié (en retrait) est encore assez impénétrable. Les gotodéclarations là - bas rendent (très) difficile la compréhension de ce que fait l'algorithme.
Jonathan Leffler
1
Étant trop jeune pour avoir vécu quelque chose de proche des cartes perforées, il était instructif de lire sur le problème de la perforation. +1
Qix - MONICA A ÉTÉ BRUÉE
20

Il n'y a rien de tel que GOTO considéré comme nocif .

GOTO est un outil, et comme tous les outils, il peut être utilisé et abusé .

Il y a, cependant, de nombreux outils dans le monde de la programmation qui ont tendance à être abusé plus que d' être utilisé , et GOTO est l' un d'entre eux. le AVEC instruction de Delphi en est une autre.

Personnellement, je n'utilise pas non plus de code typique , mais j'ai eu l'usage étrange de GOTO et WITH qui étaient garantis, et une solution alternative aurait contenu plus de code.

La meilleure solution serait que le compilateur vous avertisse simplement que le mot-clé a été corrompu , et vous devriez remplir quelques directives de pragma autour de l'instruction pour vous débarrasser des avertissements.

C'est comme dire à vos enfants de ne pas courir avec des ciseaux . Les ciseaux ne sont pas mauvais, mais leur utilisation n'est peut-être pas la meilleure façon de garder votre santé.

Lasse V. Karlsen
la source
Le problème avec GOTO est qu'il rompt les invariants importants que nous tenons pour acquis avec les langages de programmation modernes. Pour prendre un exemple, si j'appelle une fonction, nous supposons que lorsque la fonction se termine, elle rendra le contrôle à l'appelant, soit normalement, soit via un déroulement de pile exceptionnel. Si cette fonction utilise mal GOTO, alors bien sûr, ce n'est plus vrai. Cela rend très difficile de raisonner sur notre code. Il ne suffit pas d'éviter soigneusement la mauvaise utilisation de GOTO. Le problème persiste si GOTO est mal utilisé par nos dépendances ...
Jonathan Hartley
... Donc, pour savoir si nous pouvons raisonner sur nos appels de fonction, nous devons examiner chaque ligne du code source de chacune de nos dépendances transitives, en vérifiant qu'elles n'utilisent pas abusivement GOTO. Donc, l'existence de GOTO dans le langage a brisé notre capacité à raisonner en toute confiance sur notre propre code, même si nous l'utilisons parfaitement (ou ne l'utilisons jamais) nous-mêmes. Pour cette raison, GOTO n'est pas seulement un outil à utiliser avec précaution. Il est systématiquement rompu et son existence dans une langue est unilatéralement considérée comme nuisible.
Jonathan Hartley
Même si le gotomot clé a été effacé de C #, le concept de "sauter ici" existe toujours en IL. Une boucle infinie peut facilement être construite sans le mot-clé goto. Si ce manque de garantie que le code appelé reviendra, à votre avis, signifie "incapable de raisonner sur le code", alors je dirais que nous n'avons jamais eu cette capacité.
Lasse V. Karlsen
Ah, vous avez trouvé avec perspicacité la source de notre mauvaise communication. Le gotoC # n'est pas un vrai "goto" au sens originel du mot. C'est une version beaucoup plus faible qui ne permet que de sauter à l'intérieur d'une fonction. Le sens dans lequel j'utilise "goto" permet de sauter n'importe où dans le processus. Donc, bien que C # ait un mot-clé "goto", il n'a sans doute jamais eu, un vrai goto. Oui, un vrai goto est disponible au niveau IL, de la même manière que quand un langage est compilé en assembleur. Mais le programmeur est protégé de cela, incapable de l'utiliser dans des circonstances normales, donc cela ne compte pas.
Jonathan Hartley
Soit dit en passant, l'opinion que je décris ici n'est pas à l'origine la mienne, mais est au cœur du document original de Dijkstra de 1967, je pense, ré-intitulé par son éditeur "Goto considéré comme nuisible", qui est devenu un mème si souvent cité pour 50 années précisément parce qu'il était si révolutionnaire, perspicace et universellement accepté.
Jonathan Hartley
17

Cela n'a jamais été le cas, tant que vous pouviez penser par vous-même.

stesch
la source
17

Depuis que j'ai commencé à faire quelques choses dans le noyau Linux, les gotos ne me dérangent pas autant qu'autrefois. Au début, j'étais un peu horrifié de voir qu'ils (les gars du noyau) avaient ajouté des gotos dans mon code. Depuis, je me suis habitué à l'utilisation de gotos, dans certains contextes limités, et je vais maintenant les utiliser de temps en temps moi-même. En règle générale, c'est un goto qui saute à la fin d'une fonction pour effectuer une sorte de nettoyage et de renflouement, plutôt que de dupliquer ce même nettoyage et renflouement à plusieurs endroits de la fonction. Et généralement, ce n'est pas quelque chose d'assez grand pour être transféré à une autre fonction - par exemple, la libération de certaines variables localement (k) malloc'ed est un cas typique.

J'ai écrit du code qui n'utilisait setjmp / longjmp qu'une seule fois. C'était dans un programme de séquenceur de batterie MIDI. La lecture s'est produite dans un processus distinct de toutes les interactions de l'utilisateur, et le processus de lecture a utilisé la mémoire partagée avec le processus d'interface utilisateur pour obtenir les informations limitées dont il avait besoin pour faire la lecture. Lorsque l'utilisateur a voulu arrêter la lecture, le processus de lecture a simplement fait un longjmp "retour au début" pour recommencer, plutôt que quelques déroulements compliqués de l'endroit où il se trouvait quand l'utilisateur voulait qu'il s'arrête. Cela a fonctionné très bien, était simple, et je n'ai jamais eu de problèmes ou de bugs liés à cela dans ce cas.

setjmp / longjmp ont leur place - mais cet endroit est celui que vous ne visiterez probablement pas, mais de temps en temps.

Edit: je viens de regarder le code. C'est en fait siglongjmp () que j'ai utilisé, pas longjmp (pas que ce soit un gros problème, mais j'avais oublié que siglongjmp existait même.)

smcameron
la source
15

Si vous écrivez une machine virtuelle en C, il s'avère que l'utilisation de gotos calculés (gcc's) comme ceci:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

fonctionne beaucoup plus rapidement que le commutateur conventionnel à l'intérieur d'une boucle.

Vermont.
la source
Seul problème, que ce n'est pas standard, n'est-ce pas?
Calmarius
&&op_incne compile certainement pas, car (la gauche) &attend une valeur l, mais (la droite) &donne une valeur r.
fredoverflow
7
@FredO: C'est un opérateur spécial du GCC. Cependant, je rejetterais ce code dans toutes les circonstances sauf les plus sombres, car je ne peux certainement pas comprendre que wtf se passe.
Puppy
Bien qu'il s'agisse d'un exemple (optimisation maximale pour la vitesse) qui justifie à la fois l'utilisation de goto et du code cryptique, il devrait tout de même être fortement commenté pour expliquer le besoin d'optimisation, à peu près comment cela fonctionne et la meilleure ligne par ligne. commentaires qui peuvent être faits. Donc, il échoue toujours, mais parce qu'il laisse les programmeurs de maintenance dans le noir. Mais j'aime l'exemple VM. Merci.
MicroservicesOnDDD
14

Parce que gotopeut être utilisé pour confondre la métaprogrammation

Gotoest à la fois une expression de contrôle de haut niveau et de bas niveau , et par conséquent, elle n'a tout simplement pas de modèle de conception approprié adapté à la plupart des problèmes.

C'est de bas niveau dans le sens où un goto est une opération primitive qui implémente quelque chose de supérieur comme whileou foreachou quelque chose.

Il est de haut niveau dans le sens où, lorsqu'il est utilisé de certaines manières, il prend du code qui s'exécute dans une séquence claire, de manière ininterrompue, à l'exception des boucles structurées, et il le transforme en morceaux de logique qui sont, avec suffisamment de gotos, une capture -Sac de logique en cours de remontage dynamique.

Il y a donc un côté prosaïque et un côté mauvaisgoto .

Le côté prosaïque est qu'un goto pointant vers le haut peut implémenter une boucle parfaitement raisonnable et un goto pointant vers le bas peut faire un breakou parfaitement raisonnable return. Bien sûr, un réel while, breakou returnserait beaucoup plus lisible, car le pauvre humain n'aurait pas à simuler l'effet dugoto pour obtenir une vue d'ensemble. Donc, une mauvaise idée en général.

Le mauvais côté implique une routine n'utilisant pas goto pendant un certain temps, une pause ou un retour, mais l'utilisant pour ce qu'on appelle la logique des spaghettis . Dans ce cas, le développeur goto-happy construit des morceaux de code à partir d'un labyrinthe de goto, et la seule façon de le comprendre est de le simuler mentalement dans son ensemble, une tâche terriblement fatigante quand il y a beaucoup de goto. Je veux dire, imaginez la difficulté d'évaluer le code où le elsen'est pas précisément un inverse du if, où les ifs imbriqués pourraient permettre certaines choses qui ont été rejetées par l'extérieur if, etc., etc.

Enfin, pour vraiment couvrir le sujet, il convient de noter que pratiquement toutes les langues anciennes, à l'exception de l'algol, ne faisaient initialement que des déclarations uniques soumises à leurs versions de if-then-else. Donc, la seule façon de faire un bloc conditionnel était de le gotocontourner en utilisant un conditionnel inverse. Fou, je sais, mais j'ai lu quelques vieilles spécifications. N'oubliez pas que les premiers ordinateurs ont été programmés en code machine binaire, donc je suppose que n'importe quel type de HLL a été une bouée de sauvetage; Je suppose qu'ils n'étaient pas trop pointilleux sur les fonctionnalités HLL qu'ils avaient.

Après avoir dit tout ce que j'avais l'habitude d'en coller un gotodans chaque programme, j'ai écrit "juste pour ennuyer les puristes" .

DigitalRoss
la source
4
+1 pour ennuyer les puristes! :-)
Lumi
10

Refuser l'utilisation de l'instruction GOTO aux programmeurs revient à dire à un menuisier de ne pas utiliser de marteau car cela pourrait endommager le mur pendant qu'il enfonce un clou. Un vrai programmeur sait comment et quand utiliser un GOTO. J'ai suivi certains de ces soi-disant «programmes structurés». J'ai vu un tel code horrible juste pour éviter d'utiliser un GOTO, que j'ai pu tirer sur le programmeur. Ok, pour défendre l'autre côté, j'ai vu du vrai code spaghetti aussi et encore, ces programmeurs devraient aussi être abattus.

Voici juste un petit exemple de code que j'ai trouvé.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

-----------------------OU----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10
CurtTampa
la source
3
DO CRT 'Est-ce correct? (O / N): «: ENTRER LA CORNE JUSQU'À LA CORNE =« Y »OU LA CORNE =« N »; etc.
joel.neely le
5
En effet, mais plus important encore, un vrai programmeur sait quand ne pas utiliser un goto- et sait pourquoi . Éviter une construction de langage tabou parce que $ programming_guru l'a dit, c'est la définition même de la programmation cargo-culte.
Piskvor a quitté le bâtiment le
1
C'est une bonne analogie. Pour enfoncer un clou sans endommager le substrat, vous n'éliminez pas le marteau. Vous utilisez plutôt un outil simple appelé poinçon à ongles. Il s'agit d'une broche métallique avec une extrémité effilée qui a une indentation creuse à son extrémité, pour se coupler en toute sécurité avec la tête du clou (ces outils sont disponibles en différentes tailles pour différents ongles). L'autre extrémité émoussée du poinçon est frappée avec un marteau.
Kaz
9

"Dans ce lien http://kerneltrap.org/node/553/2131 "

Ironiquement, l'élimination du goto a introduit un bug: l'appel spinlock a été omis.

Jay Ballou
la source
+1 pour 'l'élimination du goto a introduit un bug'
QED
7

Le document original doit être considéré comme "GOTO inconditionnel considéré comme nuisible". Il préconisait en particulier une forme de programmation basée sur des constructions conditionnelles ( if) et itératives ( while), plutôt que sur le test-and-jump commun au code ancien. gotoest toujours utile dans certaines langues ou circonstances, où aucune structure de contrôle appropriée n'existe.

John Millikin
la source
6

À propos du seul endroit où je suis d'accord Goto pourrait être utilisé est le moment où vous devez traiter les erreurs, et chaque point particulier où une erreur se produit nécessite une gestion particulière.

Par exemple, si vous saisissez des ressources et utilisez des sémaphores ou des mutex, vous devez les saisir dans l'ordre et vous devez toujours les libérer de la manière opposée.

Certains codes nécessitent un modèle très étrange de récupération de ces ressources, et vous ne pouvez pas simplement écrire une structure de contrôle facilement maintenue et comprise pour gérer correctement à la fois la récupération et la libération de ces ressources pour éviter un blocage.

Il est toujours possible de le faire correctement sans goto, mais dans ce cas et quelques autres, Goto est en fait la meilleure solution principalement pour la lisibilité et la maintenabilité.

-Adam

Adam Davis
la source
5

Une utilisation GOTO moderne est par le compilateur C # pour créer des machines d'état pour les énumérateurs définis par return return.

GOTO est quelque chose qui devrait être utilisé par les compilateurs et non par les programmeurs.

Brian Leahy
la source
5
Selon vous, qui crée exactement les compilateurs?
tloach
10
Compilateurs, bien sûr!
Seiti
3
Je pense qu'il veut dire "GOTO est quelque chose qui ne devrait être utilisé que par du code émis par un compilateur".
Simon Buchan
C'est un cas contre goto. Là où nous pourrions utiliser gotodans une machine d'état codée à la main pour implémenter un énumérateur, nous pouvons simplement utiliser à la yieldplace.
Jon Hanna
activer plusieurs chaînes (pour empêcher la compilation en if-else) avec les compilations de cas par défaut pour basculer avec l'instruction goto.
M.kazem Akhgary
5

Jusqu'à ce que C et C ++ (entre autres coupables) aient étiqueté les ruptures et continuent, goto continuera d'avoir un rôle.

DrPizza
la source
Donc, étiqueter pause ou continuer serait différent de goto comment?
Matthew Whited
3
Ils ne permettent pas des sauts totalement arbitraires dans le flux de contrôle.
DrPizza
5

Si GOTO lui-même était mauvais, les compilateurs seraient mauvais, car ils génèrent des JMP. Si sauter dans un bloc de code, en particulier après un pointeur, était intrinsèquement mauvais, l'instruction RETurn serait mauvaise. Au contraire, le mal est dans le potentiel d'abus.

Parfois, j'ai dû écrire des applications qui devaient suivre un certain nombre d'objets où chaque objet devait suivre une séquence complexe d'états en réponse à des événements, mais le tout était définitivement monofil. Une séquence typique d'états, si elle est représentée en pseudo-code, serait:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Je suis sûr que ce n'est pas nouveau, mais la façon dont je l'ai géré en C (++) était de définir des macros:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Ensuite (en supposant que l'état est initialement 0), la machine à états structurée ci-dessus se transforme en code structuré:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

Avec une variation à ce sujet, il peut y avoir APPEL et RETOUR, donc certaines machines d'état peuvent agir comme des sous-routines d'autres machines d'état.

Est-ce inhabituel? Oui. Faut-il un peu d'apprentissage de la part du responsable? Oui. Cet apprentissage est-il payant? Je le pense. Pourrait-il être fait sans GOTO qui saute en blocs? Nan.

Mike Dunlavey
la source
1
Éviter une fonction linguistique par peur est un signe de lésion cérébrale. C'est aussi plus nift que l'enfer.
Vector Gorgoth
4

Je l'évite car un collègue / manager remettra sans aucun doute en question son utilisation soit dans une révision de code, soit lorsqu'ils tomberont dessus. Bien que je pense qu'il a des utilisations (le cas de la gestion des erreurs par exemple) - vous rencontrerez un autre développeur qui aura un problème avec lui.

Ça ne vaut pas le coup.

Aardvark
la source
La bonne chose à propos des blocs Try ... Catch en C # est qu'ils s'occupent du nettoyage de la pile et des autres ressources allouées (appelées déroulement de la pile) lorsque les bulles d'exception atteignent un gestionnaire d'exceptions. Cela fait un Try ... Catch beaucoup mieux que Goto, donc si vous avez Try ... Catch, utilisez-le.
Scott Whitlock
J'ai une meilleure solution: envelopper le morceau de code dont vous voulez sortir dans un 'do {...} while (0);' boucle. De cette façon, vous pouvez sauter de la même manière qu'un goto sans la surcharge de la boucle try / catch (je ne sais pas pour C #, mais en C ++ try est à faible coût et catch est à coût élevé, il semble donc excessif pour lever une exception où un simple saut suffirait).
Jim Dovey
10
Jim, le problème est que ce n'est rien de plus qu'un moyen stupidement détourné d'obtenir un goto.
Codage avec style
4

En fait, je me suis retrouvé obligé d'utiliser un goto, car je ne pouvais littéralement pas penser à une meilleure façon (plus rapide) d'écrire ce code:

J'avais un objet complexe et j'avais besoin de faire une opération dessus. Si l'objet était dans un état, alors je pouvais faire une version rapide de l'opération, sinon je devais faire une version lente de l'opération. Le fait était que dans certains cas, au milieu de l'opération lente, il était possible de réaliser que cela aurait pu être fait avec l'opération rapide.

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

C'était dans un morceau critique de vitesse du code de l'interface utilisateur en temps réel, donc je pense honnêtement qu'un GOTO était justifié ici.

Hugo

Rocketmagnet
la source
La méthode non GOTO serait d'utiliser une fonction fast_calculations, ce qui entraîne des frais généraux. Probablement pas perceptible dans la plupart des circonstances, mais comme vous l'avez dit, c'était critique en termes de vitesse.
Kyle Cronin
1
Eh bien, ce n'est guère surprenant. Tout le code qui a des directives de performances absolument folles violera toujours à peu près toutes les meilleures pratiques. Les meilleures pratiques concernent l'accessibilité et la maintenabilité, et non pas la compression de 10 millisecondes de plus ou la sauvegarde de 5 octets de RAM supplémentaire.
Jonathon
1
@JonathonWisnoski, les utilisations légitimes de goto éliminent également des quantités folles de code spagetti se mêlant aux nids de variables d'un rat pour garder une trace où nous allons.
vonbrand
4

Presque toutes les situations où un goto peut être utilisé, vous pouvez faire de même en utilisant d'autres constructions. Goto est de toute façon utilisé par le compilateur.

Personnellement, je ne l'utilise jamais explicitement, je n'en ai jamais besoin.

Adam Houldsworth
la source
4

Une chose que je n'ai vu dans aucune des réponses ici, c'est qu'une solution «goto» est souvent plus efficace qu'une des solutions de programmation structurée souvent mentionnées.

Considérons le cas des boucles imbriquées multiples, où l'utilisation de «goto» au lieu d'un tas de if(breakVariable) sections est évidemment plus efficace. La solution "Mettez vos boucles dans une fonction et utilisez return" est souvent totalement déraisonnable. Dans le cas probable où les boucles utilisent des variables locales, vous devez maintenant les passer toutes par le biais de paramètres de fonction, gérant potentiellement des charges de maux de tête supplémentaires qui en découlent.

Considérons maintenant le cas de nettoyage, que j'ai moi-même utilisé assez souvent, et qui est si courant qu'il est vraisemblablement responsable de la structure try {} catch {} non disponible dans de nombreuses langues. Le nombre de vérifications et de variables supplémentaires nécessaires pour accomplir la même chose est bien pire que la ou les deux instructions pour effectuer le saut, et encore une fois, la solution de fonction supplémentaire n'est pas du tout une solution. Vous ne pouvez pas me dire que c'est plus gérable ou plus lisible.

Désormais, l'espace de code, l'utilisation de la pile et le temps d'exécution peuvent ne pas être assez importants dans de nombreuses situations pour de nombreux programmeurs, mais lorsque vous êtes dans un environnement intégré avec seulement 2 Ko d'espace de code pour travailler, 50 octets d'instructions supplémentaires pour éviter une définition claire 'goto' est juste risible, et ce n'est pas une situation aussi rare que le croient de nombreux programmeurs de haut niveau.

La déclaration selon laquelle «goto est nuisible» a été très utile pour évoluer vers une programmation structurée, même si elle a toujours été une généralisation excessive. À ce stade, nous l'avons tous suffisamment entendu pour ne pas l'utiliser (comme nous le devrions). Quand c'est évidemment le bon outil pour le travail, nous n'avons pas besoin d'en avoir peur.

gkimsey
la source
3

Vous pouvez l'utiliser pour interrompre une boucle profondément imbriquée, mais la plupart du temps, votre code peut être refactorisé pour être plus propre sans boucles profondément imbriquées.

Brian R. Bondy
la source