Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Goto : une instruction pas comme les autres ?
Le débat autour de l'instruction refait surface suite au « Goto fail » d'iOS et de GnuTLS

Le , par Arsene Newman

83PARTAGES

4  5 
Suite à la découverte récente de bugs à la fois sous Linux et sous iOS/OS X, tous les deux dus à une utilisation de l’instruction Goto du langage C, la question de l’utilisation de cette instruction refait surface.

Goto est une instruction héritée des premiers langages informatiques, époque où certaines instructions très connues actuellement n’existaient pas, comme les boucles et les structures de contrôle. Néanmoins, même à l’âge moderne de l’informatique et après avoir été décriée par plusieurs informaticiens de renom à l’instar de Djikistra, cette instruction est encore utilisée et cela même au sein des géants de l’informatique comme Apple ; à titre d’exemple la recherche du mot clé Goto sur un portail tel que GitHub débouche sur des millions de résultats.

Alors dans quel cas cette instruction est encore utilisée ? Parmi les cas de figure évoqués existent la sortie d’une boucle imbriquée ce qui épargne le recours à plusieurs break, l’amélioration de la lisibilité du code et le traitement des erreurs ou encore l’optimisation manuelle du code pour améliorer les performances.

Toutefois, alors qu’une utilisation parcimonieuse dans les deux premiers cas semble correcte, cela s’avère incorrect pour le dernier cas, selon Jeff Law et Jason Merril, tous les deux ingénieurs chez Red Hat et membres du comité du compilateur GCC. En effet ils expliquent que l’optimisation manuelle du code n’est plus à l’ordre du jour car les compilateurs modernes sont suffisamment développés pour se charger gracieusement de cette tâche, en transformant le code en entrée en une série de blocs de base et en se reposant sur l’utilisation d’un graphe de flot de contrôle (GFC). Résultat des courses aucune distinction entre un code bien structuré et un code en spaghetti (qui résulte d’une addiction au goto).

Autre argument en faveur de cette thèse, l’article du célèbre Donald Knuth sur le sujet à savoir « Structured Programming with Go To Statements », où il explique qu’une optimisation prématurée du code ne peut déboucher que sur une utilisation malsaine de Goto.

Au final, la recommandation de l’utilisation du Goto n’est toujours pas à l’ordre du jour, mais cette instruction héritée des premiers âges de l’informatique garde encore sa place dans des cas précis où certains langages, à l’image du langage C, affichent des manques et des carences, ce qui ne cadre pas avec un quelconque souci d’optimisation manuelle du code.

Source : billet de Larry Seltzer

Et vous ?

Qu’en pensez-vous ?

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de Obsidian
Modérateur https://www.developpez.com
Le 14/03/2014 à 11:32
Citation Envoyé par Arsene Newman Voir le message
Suite à la découverte récente de bugs à la fois sous Linux et sous iOS/OS X, tous les deux dus à une utilisation de l’instruction Goto du langage C, la question de l’utilisation de cette instruction refait surface.
Ce n'est pas lié au goto, mais au fait qu'une ligne s'est trouvée dupliquée. 'faudrait voir à ne pas troller plus encore qu'on le fait déjà à ce sujet. Il aurait bien pu se passer la même chose avec un point-virgule mal placé, ce qui est assez fréquent en C.

Goto est une instruction héritée des premiers langages informatiques, époque où certaines instructions très connues actuellement n’existaient pas, comme les boucles et les structures de contrôles. Néanmoins, même à l’âge moderne de l’informatique et après avoir été décrié par plusieurs informaticiens de renom à l’instar de Djikistra, cette instruction est encore utilisée et cela même au sein des géants de l’informatique comme Apple, à titre d’exemple la recherche du mot clé Goto sur un portail tel que GitHub débouche sur des millions de résultats.
Paradoxalement, c'est en BASIC 512 et en GW BASIC que j'ai rencontré les pires incompatibilités avec, parce que le langage était interprété et que la fin d'une boucle DO…LOOP ou FOR…NEXT était matérialisée par une instruction, laquelle pouvait en plus être éventuellement soumise à un IF. Les structures de boucles en cours étaient alors stockées dans la pile et faire un goto ne nettoyait donc pas cette pile. Ça restait tout-à-fait légal, d'ailleurs, puisque rien ne nous interdisait de re-sauter vers l'intérieur de la boucle, même si tout cela était sale. En C, ça l'est moins justement parce que les bornes sont définies à la compilation et que le tout est résolu en une série de JMP (donc des gotos) une fois compilé.

Alors dans quel cas cette instruction est encore utilisée ? Parmi les cas de figure évoqués existent la sortie d’une boucle imbriquée ce qui épargne le recours à plusieurs break, l’amélioration de la lisibilité du code et le traitement des erreurs ou encore l’optimisation manuelle du code pour améliorer les performances.
Outre ceux cités, j'ai personnellement deux cas en tête :

1. La possibilité de sauter dans une boucle pour la faire démarrer ailleurs qu'au début, ce qui permet de gérer efficacement les cas du style « 1 + le reste » très utilisés en informatique et dans les séries mathématiques. Par exemple, pour écrire une suite de chiffres séparés par des tirets :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
x=1; goto debut;
while (x<=5)
{
    putchar ('-');
    debut:
    printf ("%d",x);
    x++;
}
… et qui donne « 1-2-3-4-5 ». Ceci nous permet d'éviter un « if() » au sein de la boucle, évalué à chaque tour simplement pour traiter le premier cas. Je me suis rendu compte a posteriori que c'était l'exemple donné dans la norme pour illustrer goto.

On remarque que ça pourrait tout-à-fait être un modèle de boucle à part entière : de même qu'il existe « continue » en C pour provoquer le saut immédiat vers la fin de la boucle et lui faire faire une itération, on pourrait tout-à-fait imaginer un mot-clé « start » servant à faire cela.

2. Les automates finis à états. Le goto est par nature l'âme d'un AFD, puisqu'il s'agit de sauter d'un état prédéterminé à un autre. Si le cheminement du traitement est déterminé à l'avance, il est tout-à-fait possible d'écrire :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
etat1:
    traitement1();
    if (condition) goto etat2;
    if (condition) goto etat3;
    if (condition) goto etat5;
    goto etat6;

etat2:
    traitement2();
    if (condition) goto etat6;
    if (condition) goto etat4;
    if (condition) goto etat1;
    goto etat2;

…
… dans de telles conditions, on va avoir tendance à écrire un switch() et à stocker l'état en cours dans une variable, ce qui transpose inutilement au runtime ce qui est statique à la base.

Ce que l'on reproche au goto, donc, c'est surtout d'être généralement incompatible avec la programmation structurée. Mais ça, c'est un paradigme qui doit être pensé par le programmeur avant tout. De la même façon qu'il est possible d'avoir une approche orientée objet en C même si le langage n'est pas spécialement conçu pour cela au départ, il est possible d'écrire un programme propre avec des gotos si le programmeur le souhaite. L'ennui est qu'il est généralement impossible de le faire admettre à son entourage direct et qu'en entreprise, il est plus facile d'écrire du code sale mais « orthodoxe » plutôt qu'avoir à justifier son goto.

Toutefois alors qu’une utilisation parcimonieuse dans les deux premiers cas semble correcte, cela ne s’avère pas correcte pour le dernier cas selon Jeff Law et Jason Merril tous les deux ingénieurs chez Red Hat et membre du comité du compilateur GCC, en effet ils expliquent que l’optimisation manuelle du code n’est plus à l’ordre du jour car les compilateurs modernes sont suffisamment développés pour se charger gracieusement de cette tâche, en transformant le code en entrée en une série de blocs de base et en se reposant sur l’utilisation d’un graphe de flot de contrôle (GFC), résultat des courses aucune distinction entre un code bien structuré et un code en spaghetti (qui résulte d’une addiction au goto).
Il faudrait commencer par arrêter d'associer systématiquement goto et code spaghetti. D'abord parce qu'à la longue, ça devient pénible, ensuite et surtout parce que rien n'empêche un programmeur d'écrire du code spaghetti sans goto. Et précisément, une grande maladie, contemporaine cette fois, consiste à éliminer tous les gotos et les remplacer par n'importe quoi d'autre, sans se soucier de savoir si c'est réellement plus efficace (même au niveau global) et surtout si ce n'est pas pire.

Ensuite, au niveau du compilateur, c'est vrai mais dans une certaine mesure également. Il est vrai que les compilateurs sont devenus particulièrement performants mais le fait est qu'on leur prête souvent des pouvoirs magiques et que les gens qui se penchent réellement sur la qualité du code produit sont bien peu nombreux.

Au final, la recommandation de l’utilisation du Goto n’est toujours pas à l’ordre du jour mais cette instruction héritée des premiers âges de l’informatique garde encore sa place dans des cas précis où certains langages à l’image du langage C affichent des manques et des carences, ce qui ne cadre pas avec un quelconque souci d’optimisation manuelle du code.
goto, c'est comme l'utilisateur root sous Unix : c'est l'instruction omni-potente. Il faut juste veiller à ne pas l'utiliser pour se sortir d'une impasse (et c'est surtout ça qu'il faut surveiller à mon avis) mais ça permet d'implémenter facilement ce qui n'a pas été prévu au départ.

Citation Envoyé par Bktero Voir le message
Mais le fait que quasiment personne ne s'en sert montre qu'on peut presque toujours faire autrement.
Un vrai débat de fond, voire un sujet d'études scientifique, serait d'auditer un grand nombre de programmes chez les codeurs de tous niveaux et vérifier si, à chaque fois, la solution alternative adoptée est réellement meilleure. Mais ça…
17  1 
Avatar de Obsidian
Modérateur https://www.developpez.com
Le 14/03/2014 à 12:56
Citation Envoyé par valkirys Voir le message
Même un if then else sera compilé avec des goto, le but premier des langages ( structurés ) est de simplifier le code source en supprimant les goto qui pique les yeux...
La vraie question est : pourquoi ça te pique les yeux (à part le fait que c'est ce que l'on t'a toujours dit) ? Les réponses existent et elles sont à la base de ce débat, mais elles sont discutables en fonction de la situation et surtout, plus personne ne se pose cette question, même au départ.

Et c'est bien le problème : l'objectif des codeurs n'est plus d'écrire le code le propre possible en fonction du contexte mais bien d'éviter les goto par principe.

Ok pour le deuxième mais le premier se réécrit :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
printf ("%d",1);
x=2;
while (x<5)
{
    putchar ('-');
    printf ("%d",x);
    x++;
}
je ne vois pas l’intérêt du goto.
Justement pour éviter d'avoir à écrire deux fois la même chose, uniquement pour prendre en compte le cas initial !

Dans cet exemple, tu saisis deux fois « printf() » mais tu n'établis aucune relation logique entre les deux : le compilateur n'a aucun moyen de savoir que ton premier printf() est en fait censé faire partie de la même boucle, ce qui casse complètement le principe-même de la programmation structurée. Ensuite, même si ce n'était pas nécessaire, tu as changé la condition initiale (x=2 et plus x=1) et quitte à écrire printf("%d",1);, tu aurais pu directement écrire printf("1";. Donc, des erreurs en cascade induites par la volonté de se priver du goto et qui, au final, rendent le code moins bon qu'au départ.

Ensuite, dans le cas présent, il n'y a qu'une seule instruction. Que se passerait-il si tu devais sauter les cinquante premières instructions d'une boucle qui en compte cent ? Pour adopter le modèle que tu nous présentes, tu serais obligé de déclarer une fonction pour les appeler facilement avant puis dans la boucle. Et si tu retrouves le même cas de figure trente fois dans ton programme, tu dois déclarer trente fonctions locales. C'est idiot.

C'est également plus pénible pour le développeur : même s'il reconnaît ce que tu es en train de faire, il ne peut pas savoir s'il y a bien une liaison implicite entre tes deux printf. Ça devient critique lorsque tu fais la maintenance d'un grand programme : lorsque tu en vient à modifier ce printf pour le mettre à jour ou le remplacer par autre chose, tu introduis automatiquement un bug si tu ne penses pas à traiter l'autre.

Il est intéressant, lorsque l'on développe du logiciel, d'essayer de transposer cela au reste de l'industrie et, en particulier, de penser à la manière dont on réaliserait la fonction de façon mécanique plutôt que logicielle : dans le cas présent, si je veux imprimer sur une feuille une suite de motifs séparés par des tirets, il me suffit de construire un simple rouleau d'imprimerie et de le « déphaser » de manière à ce qu'il commence par le motif plutôt que par le tiret. La solution que tu nous proposes consisterait, elle, à mettre en place une machine spéciale dédiée pour imprimer le premier motif uniquement avant de faire passer normalement la feuille dans le rouleau.

Tout cela pour éviter un goto qui aurait eu tout-à-fait sa place ici.

C'est intéressant parce qu'en général, on en arrive à dire « de toutes façons le compilateur va optimiser tout cela ». Personne ne nous le garantit, d'une part, et cela revient à dire que le vrai travail est en fait mené par les personnes qui ont conçu le compilateur. Et même alors, le compilateur sera à même de faire cette optimisation que s'il est capable de reconnaître le modèle. Il faut donc que celui-ci soit défini au départ et fasse partie des motifs qui lui ont été enseignés. En toute rigueur, c'est surtout ce travail que le programmeur devrait faire, et utiliser les goto si le langage ne propose pas de lui-même ce modèle.

Citation Envoyé par Washmid Voir le message
Mes souvenirs en C sont un peu rouillés mais un truc du genre revient un peu au même que l'exemple en 1 :
EDIT : arf, valkirys, plus rapide que moi et sans break mais avec une ligne copiée-collée ^^
C'était justement le problème de départ : éviter de se trimballer un « if » évalué à chaque itération alors qu'il n'est là que pour tester le premier cas.
13  3 
Avatar de Médinoc
Expert éminent sénior https://www.developpez.com
Le 14/03/2014 à 14:26
Pourquoi tant de gens ici parlent de "goto pour optimisation"?

Les deux bugs récents proviennent de codes où l'usage de goto n'avait rien à voir avec l'optimisation, et tout à voir avec une gestion d'erreur+nettoyage plus lisible (sans code boomerang et sans duplication du nettoyage à chaque sortie).

Ce qui est à ma connaissance le cas le plus reconnu pour l'utilisation de goto en C.

Et franchement, la source du bug était plutôt une étiquette dont le nom ne correspondait pas au contenu (le "fail" du bug Apple faisait en fait un "cleanup" que le goto lui-même.
9  0 
Avatar de ManusDei
Expert confirmé https://www.developpez.com
Le 14/03/2014 à 14:58
La non-utilisation du GOTO n'est pas une restriction, mais une tradition, nuance.
Et il faudrait faire attention à ne pas en faire un dogme, car il existe quelques rares cas où il est indispensables, et d'autres où il est quand même bien plus clair.

PS : Sinon on devrait interdire les pointeurs manuels et leur manipulation, car niveau casse-gueule, les pointeurs c'est pas mal non plus (ça et la gestion manuelle de la mémoire).
9  0 
Avatar de picodev
Membre émérite https://www.developpez.com
Le 15/03/2014 à 14:35
Bonjour,

cela faisait longtemps aussi que je n'avait répondu à un fil sur le goto !
Après m'être frotté avec des machines allant du TI99/4A à l'atari 512stf, J'ai enfin reçu une «vraie» formation informatique en fac au courant des années 1990. C'est à ce moment qu'on m'a révélé les commandements que je ne devrais jamais violer au risque de m'exposer à la colère divine. Je peux citer des choses comme «la bande passante du réseau illimitée tu ne croiras point» ou «de courtes fonctions tu écriras» sans oublier le «ton code tu commenteras». Mais il est vrai que LA loi première était «JAMAIS de goto tu n'utiliseras», LA loi qui évite de rentrer dans un état de péché originel ... le goto est comme une pomme qu'il ne faut pas croquer sous peine d'expulsion du jardin de l'Eden de la programmation structurée.
J'obéissais militairement à ces lois dans le but d'être adoubé mais essayais néanmoins d'en comprendre l'origine. Jusqu'au jour où j'ai reçu l'illumination de la part d'un de mes professeur, M. Cansell (true story).

Le goto peut introduire de la confusion dans la phase de compilation, il interrompt le flux sans qu'on puisse a priori savoir ce qui va se passer réellement ou quelle était l'intention du programmeur (du moins à l'époque). Écrire une boucle for en utilisant des gotos masquait le fait de vouloir utiliser une boucle for et empêchait le compilo de faire du bon boulot. C'est pourquoi il était préférable de toujours utiliser une boucle for ou while, ou toute autre structure de plus haut niveau pour se passer de goto. Enfin en général, car les gurus du goto pouvaient optimiser certains codes grâce à eux, mais comme nous ne sommes que des mortels faillibles et non des gurus ....
Et effectivement, au fur et à mesure, les cas d'utilisation de goto se sont retrouvées de moins en moins nombreux car remplacés par de nouvelles structure de niveau supérieur : switch (goto calculé), gestion des erreurs, etc ...
La règle est alors amendée : si le langage utilisé ne propose pas la structure de contrôle adaptée alors le goto avec des pincettes tu pourras utiliser. Par exemple le C ne propose pas de gestion d'erreur par try/catch ... ok on peut utiliser les goto si c'est indispensable, C ne propose pas un «ne fait pas ça pour au premier tour de boucle» du coup un goto est «autorisé» et même élégant dans ce cas je trouve car ça évite une duplication de code (encore une loi ... jamais inutilement du code tu ne dupliqueras).

Le second point est, comme le soulève Obsidian, celui des automates finis, et plus généralement des parsers. Les machines à états avec transitions s'implémentent agréablement avec des gotos ... mais souvent la taille des programmes est telle que le code devient complexe sans pour autant être compliqué. La solution la plus simple est donc de créer un programme (et de prouver qu'il est correct) qui va s"en charger pour nous. Lex,flex,bison et yacc tirent leur origine de ça et créent du code bourré de goto (enfin dans les années 90...) tout à fait correct (la plupart du temps dans les années 90 ).

Bref, un goto c'est comme une paire de ciseaux pointus : on se blesse plus souvent qu'avec une paire de ciseaux arrondis.
9  0 
Avatar de Obsidian
Modérateur https://www.developpez.com
Le 14/03/2014 à 14:03
Avant tout : je m'aperçois en me relisant que mon ton était peut-être un peu sec. Mes excuses à ce que j'aurais pu blesser, ce n'était pas intentionnel. Nous sommes bien en train de disserter sur le fond et rien d'autre.

Citation Envoyé par valkirys Voir le message
OnIdot? Je comprends bien à la lecture des derniers paragraphes votre façon de faire très mécanique, mais je trouve normal de découper un algorithme en une phase d'initialisation+ traitement des cas puis une partie automatique (boucle) c'est plus clair.
Oui mais justement : le premier tour de boucle ne constitue pas une phase d'initialisation. C'est la même que celle des autres tours et l'une et l'autre doivent être modifiées en même temps si c'est nécessaire.

Et si il faut introduire des fonctions alors c'est d'accord pour moi.
En quoi — dans le cas présent — est-ce plus propre qu'un goto ?

( pour le printf("%d",1);, on est d'accord c'est un exemple écrit vite fait )
On est bien d'accord et c'est bien comme cela que je l'ai vu aussi : cela dit, c'est révélateur d'un problème assez fréquent : introduire des horreurs, volontairement ou non, pour corriger des choses qui n'ont pas besoin de l'être. C'est très fréquent en développement logiciel.

Il manque des commentaires expliquant que algorithmes à deux partis inutilisation et boucle. Et bien sûr il ne faudrait pas des 5 codé en dur...
Les commentaires sont un autre débat mais là encore, le problème de fond est différent : il s'agit bien d'un modèle de boucle à part entière et pas d'une initialisation préalable. Dans le cas du rouleau d'imprimerie, l'initialisation consisterait justement à placer le rouleau dans la bonne position avant de lancer la machine. Pas à imprimer le motif.

J'évite les goto ( sauf boucles imbriqués ) par principe mais pas pour ces raisons là.
Justement : la sortie d'une boucle imbriquée avec goto, ça se pratique parce qu'il n'existe pas de « break n ». On en revient à ce que l'on disait : on définit un modèle clair au départ et on l'implémente avec des gotos quand le langage ne le propose pas lui-même.

Maintenant, comme on l'a dit également, la sortie de boucle est un modèle qui marche très bien en C mais qui n'est pas du tout universel et qui pose problème justement avec les langages interprétés, à commencer par le BASIC où le goto, s'il n'en est pas carrément issu, est censé être roi.
8  0 
Avatar de Grimly
Membre averti https://www.developpez.com
Le 14/03/2014 à 14:20
L'instruction GOTO ou JUMP suivant comment chacun l'a appris est à la base de nos structures IF, FOR, WHILE et UNTIL.
Il est tout aussi possible de réaliser des bugs avec une instruction GOTO en l'utilisant mal qu'avec une structure WHILE.
Il reste à chacun d'utiliser ce avoir quoi il est le plus à l'aise et confiant. Blâmer l'un ou l'autre n'a aucun sens.

On pourrait aussi parler de l'utilité des instructions CONTINUE et BREAK disponibles dans la majorité des langage largement utilisés, qui ne sont que des GOTO avec comme restriction de n'agir que dans le cadre d'une instruction FOR, WHILE ou UNTIL.
7  0 
Avatar de
https://www.developpez.com
Le 14/03/2014 à 12:29
Citation Envoyé par Bktero Voir le message
goto peut avoir ses intérêts dans certains cas qui sont assez bien identifiés. Mais le fait que quasiment personne ne s'en sert montre qu'on peut presque toujours faire autrement. L'optimisation manuelle du code ne concerne même pas goto en particulier.
Même un if then else sera compilé avec des goto, le but premier des langages ( structurés ) est de simplifier le code source en supprimant les goto qui pique les yeux...

Citation Envoyé par Obsidian Voir le message

Code : Sélectionner tout
1
2
3
4
5
6
7
8
x=1; goto debut;
while (x<5)
{
    putchar ('-');
    debut:
    printf ("%d",x);
    x++;
}
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
etat1:
    traitement1();
    if (condition) goto etat2;
    if (condition) goto etat3;
    if (condition) goto etat5;
    goto etat6;

etat2:
    traitement2();
    if (condition) goto etat6;
    if (condition) goto etat4;
    if (condition) goto etat1;
    goto etat2;

…
Ok pour le deuxième mais le premier se réécrit :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
printf ("%d",1);
x=2;
while (x<5)
{
    putchar ('-');
    printf ("%d",x);
    x++;
}
je ne vois pas l’intérêt du goto.
6  0 
Avatar de germinolegrand
Membre expert https://www.developpez.com
Le 14/03/2014 à 14:11
Pour ma part je suis en train de faire des tests en C++, et le goto s'en sort vraiment très bien au milieu des exceptions et des destructeurs.
J'en écrirai plus long sur le sujet quand j'aurai suffisamment fait de tests, mais pour l'instant il me semble que l'on pourrait (en C++, pas en C attention) le dédiaboliser, car il apporte la solution à un certains nombre de problèmes qui sont actuellement résolus par des workaround bien moches à coups de boucles.
5  0 
Avatar de CodeurPlusPlus
En attente de confirmation mail https://www.developpez.com
Le 14/03/2014 à 15:41
Les pointeurs c'est la liberté. Dans un langage avec pointeur, le programmeur décide si un type de données qu'il crée est un pointeur explicite, un pointeur implicite ou pas un pointeur du tout. Dans un langage sans pointeur, la règle est la sémantique par référence uniforme, et il faut s'y plier que cela nous arrange ou non.

Quand j'étais étudiant, un de nos profs avait écrit un module en pur langage C qui offrait une implémentation tout à fait bonne des exceptions dans ce langage. Quand on regardait le code de ce module, il y avait quelques goto dedans, et il aurait été difficile de s'en passer.

Cela dit évidemment qu'un programmeur n'a généralement pas besoin d'utiliser des gotos.

EDIT : l'intervention de 11h32 de Obsidian est hyper intéressante. J'ai appris des trucs.
5  0