Envoyé par
Arsene Newman
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 :
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 :
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.
Envoyé par
Bktero
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 |